Przejdź do treści
School IT / Przestrzeń do nauki

MessageBox — komunikaty i pytania

Czasem komunikat w napisie to za mało: przed usunięciem danych trzeba zapytać „Czy na pewno?”, a poważny błąd powinien zatrzymać użytkownika, dopóki go nie przeczyta. Do tego służy okno komunikatu MessageBox. Zadania egzaminacyjne bardzo często go wymagają. Nauczysz się dobierać ikonę i przyciski, odczytywać odpowiedź i pytać o potwierdzenie przed zamknięciem programu.

C# XAML MessageBox MessageBoxResult Closing 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wyświetlisz komunikat z tytułem, ikoną i wybranym zestawem przycisków.
  • Dobierzesz ikonę do sytuacji: informacja, ostrzeżenie, błąd, pytanie.
  • Odczytasz odpowiedź użytkownika (MessageBoxResult) i uzależnisz od niej dalsze działanie.
  • Wyjaśnisz, co znaczy, że okno komunikatu jest modalne.
  • Zapytasz o potwierdzenie przed zamknięciem okna i w razie potrzeby anulujesz zamykanie.
  • Ocenisz, kiedy lepszy jest komunikat w oknie, a kiedy MessageBox.
TEORIA

Komunikat, którego nie da się przeoczyć

MessageBox to małe systemowe okno z tekstem, ikoną i przyciskami. Najważniejsza cecha: jest modalne — dopóki użytkownik go nie zamknie, nie może kliknąć niczego w oknie programu, a kod czeka w miejscu wywołania. Dzięki temu można zadać pytanie i zależnie od odpowiedzi wykonać dalszą część metody.

MainWindow.xaml.cs
MessageBox.Show("Zapisano zmiany.");     // najprostsza wersja: tekst i przycisk OK
Status.Text = "Gotowe";                  // ta linia wykona się dopiero po kliknięciu OK
 ✕
Zapisano zmiany.
OK

MessageBox.Show z samym tekstem — bez tytułu i ikony

MessageBox jest w przestrzeni nazw System.Windows, więc nie trzeba dopisywać żadnego using.

TEORIA

Cztery wersje Show

Metoda MessageBox.Show ma kilka przeciążeń — każde kolejne dodaje jeden parametr:

MainWindow.xaml.cs
MessageBox.Show("Tekst");
MessageBox.Show("Tekst", "Tytuł");
MessageBox.Show("Tekst", "Tytuł", MessageBoxButton.OKCancel);
MessageBox.Show("Nie wpisano nazwiska.", "Brak danych",
                MessageBoxButton.OK, MessageBoxImage.Warning);
Brak danych✕
⚠Nie wpisano nazwiska.
OK

pełna wersja: tekst, tytuł, przyciski, ikona

MessageBoxButtonPrzyciski
OKOK
OKCancelOK, Anuluj
YesNoTak, Nie
YesNoCancelTak, Nie, Anuluj
MessageBoxImageKiedy używać
Informationwszystko w porządku, informacja o wyniku
Warningcoś wymaga uwagi, ale da się kontynuować (brak danych, mało miejsc)
Erroroperacja się nie udała (błędne dane, brak pliku)
Questionpytanie, na które użytkownik ma odpowiedzieć
Nonebez ikony

Napisy na przyciskach („Tak”, „Nie”, „Anuluj”) są w języku systemu Windows — nie da się ich zmienić tą metodą.

TEORIA

Odczyt odpowiedzi

MessageBox.Show zwraca wartość typu MessageBoxResult — informację, który przycisk kliknięto. Zapisz ją w zmiennej i sprawdź warunkiem:

MainWindow.xaml.cs
private void Wyczysc_Click(object sender, RoutedEventArgs e)
{
    MessageBoxResult odpowiedz = MessageBox.Show(
        "Usunąć wszystkie notatki? Tej operacji nie da się cofnąć.",
        "Potwierdzenie",
        MessageBoxButton.YesNo,
        MessageBoxImage.Warning);

    if (odpowiedz == MessageBoxResult.Yes)
    {
        Notatki.Children.Clear();
    }
    // przy "Nie" nic się nie dzieje
}
MessageBoxResultKiedy
OK, Cancelkliknięto OK / Anuluj
Yes, Nokliknięto Tak / Nie
Cancel przy YesNoCanceltakże zamknięcie komunikatu krzyżykiem lub Esc

Pytanie zadawaj tak, żeby „Tak” było zrozumiałe

„Usunąć wszystkie notatki?” — dobrze. „Czy nie chcesz zachować notatek?” — źle: użytkownik nie wie, czy „Tak” usuwa, czy zachowuje. Pytanie o operację nieodwracalną oznaczaj ikoną Warning.

TEORIA

Kiedy MessageBox, a kiedy napis w oknie

SytuacjaLepiej
pytanie przed operacją, której nie da się cofnąćMessageBox z YesNo
wynik obliczeń, który użytkownik chce mieć przed oczaminapis w oknie
błąd w formularzu z wieloma polaminapis przy formularzu (albo MessageBox, jeśli wymaga tego arkusz)
informacja „zapisano”, „wysłano”krótki napis w oknie; MessageBox dopiero przy ważnych zdarzeniach
wymaganie z arkusza egzaminacyjnego „wyświetl okno z komunikatem”MessageBox — dokładnie z treścią podaną w arkuszu

Zbyt wiele okien komunikatów męczy użytkownika — po kilku zaczyna klikać OK bez czytania. Zachowaj je na sytuacje, które naprawdę wymagają uwagi.

TEORIA

Pytanie przy zamykaniu okna

Gdy użytkownik klika krzyżyk, okno wysyła zdarzenie Closing — zanim się zamknie. Jego parametr ma właściwość Cancel: ustawienie jej na true zatrzymuje zamykanie. Zdarzenie podpinasz w znaczniku <Window>:

MainWindow.xaml
<Window x:Class="Lekcja08.MainWindow"
        …
        Closing="Window_Closing">
MainWindow.xaml.cs
// na górze pliku: using System.ComponentModel;   (CancelEventArgs)

private void Window_Closing(object sender, CancelEventArgs e)
{
    MessageBoxResult odpowiedz = MessageBox.Show("Zamknąć program?", "Wyjście",
                                                 MessageBoxButton.YesNo, MessageBoxImage.Question);
    if (odpowiedz == MessageBoxResult.No)
    {
        e.Cancel = true;     // okno zostaje otwarte
    }
}

Okno zamkniesz też z kodu metodą Close() — np. przyciskiem Wyjście. Zdarzenie Closing wywoła się wtedy tak samo jak przy krzyżyku.

PRZYKŁAD

Zapisy na wycieczkę

Na wycieczkę jest 12 miejsc. Uczeń wpisuje imię i nazwisko oraz liczbę osób (1–4). Brak danych to ostrzeżenie, błędna liczba — błąd, brak miejsc — ostrzeżenie. Przed zapisaniem program pyta o potwierdzenie. Po zajęciu wszystkich miejsc pojawia się informacja. Przy próbie zamknięcia okna z zapisami na liście program pyta, czy na pewno.

Zapisy na wycieczkę–□✕
Wycieczka do Gdańska — 12 czerwca
Wolne miejsca: 7 z 12
Imię i nazwisko:
Ola Nowak
Liczba osób (1–4):
2
Zapisz
Lista zapisanych:
1. Jan Kowalski — 3 os.
2. Kasia Wiśniewska — 2 os.
Potwierdzenie✕
?Zapisać: Ola Nowak (2 os.)?
Tak
Nie

po kliknięciu „Zapisz” program czeka na odpowiedź w oknie pytania

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja08.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Zapisy na wycieczkę" Height="560" Width="400"
        WindowStartupLocation="CenterScreen"
        Closing="Window_Closing">

    <StackPanel Margin="15">
        <TextBlock Text="Wycieczka do Gdańska — 12 czerwca"
                   FontSize="18" FontWeight="Bold" TextWrapping="Wrap"/>
        <TextBlock x:Name="Miejsca" Foreground="Gray" Margin="0,0,0,12"/>

        <TextBlock Text="Imię i nazwisko:"/>
        <TextBox x:Name="Osoba" Margin="0,2,0,8"/>

        <TextBlock Text="Liczba osób (1–4):"/>
        <TextBox x:Name="Liczba" Width="60" HorizontalAlignment="Left" Margin="0,2,0,10"/>

        <Button Content="Zapisz" Padding="10,4" HorizontalAlignment="Left"
                IsDefault="True" Click="Zapisz_Click"/>

        <TextBlock Text="Lista zapisanych:" FontWeight="Bold" Margin="0,15,0,4"/>
        <StackPanel x:Name="Lista"/>
    </StackPanel>
</Window>
MainWindow.xaml.cs — pełny plik
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace Lekcja08;

public partial class MainWindow : Window
{
    private const int LimitMiejsc = 12;
    private int zajete = 0;

    public MainWindow()
    {
        InitializeComponent();
        PokazMiejsca();
    }

    private void Zapisz_Click(object sender, RoutedEventArgs e)
    {
        string osoba = Osoba.Text.Trim();
        if (osoba == "")
        {
            MessageBox.Show("Wpisz imię i nazwisko.", "Brak danych",
                            MessageBoxButton.OK, MessageBoxImage.Warning);
            Osoba.Focus();
            return;
        }

        if (!int.TryParse(Liczba.Text, out int liczba) || liczba < 1 || liczba > 4)
        {
            MessageBox.Show("Liczba osób musi być liczbą od 1 do 4.", "Błędne dane",
                            MessageBoxButton.OK, MessageBoxImage.Error);
            Liczba.Focus();
            Liczba.SelectAll();
            return;
        }

        if (zajete + liczba > LimitMiejsc)
        {
            MessageBox.Show($"Zostało tylko {LimitMiejsc - zajete} wolnych miejsc.", "Brak miejsc",
                            MessageBoxButton.OK, MessageBoxImage.Warning);
            return;
        }

        // Pytanie: program czeka, aż użytkownik kliknie Tak albo Nie
        MessageBoxResult odpowiedz = MessageBox.Show(
            $"Zapisać: {osoba} ({liczba} os.)?", "Potwierdzenie",
            MessageBoxButton.YesNo, MessageBoxImage.Question);

        if (odpowiedz != MessageBoxResult.Yes)
        {
            return;
        }

        zajete += liczba;

        TextBlock wpis = new TextBlock();
        wpis.Text = $"{Lista.Children.Count + 1}. {osoba} — {liczba} os.";
        Lista.Children.Add(wpis);

        Osoba.Clear();
        Liczba.Clear();
        PokazMiejsca();

        if (zajete == LimitMiejsc)
        {
            MessageBox.Show("Wszystkie miejsca są już zajęte.", "Komplet",
                            MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }

    private void PokazMiejsca()
    {
        Miejsca.Text = $"Wolne miejsca: {LimitMiejsc - zajete} z {LimitMiejsc}";
    }

    // Wywołuje się, gdy użytkownik próbuje zamknąć okno
    private void Window_Closing(object sender, CancelEventArgs e)
    {
        if (Lista.Children.Count == 0)
        {
            return;
        }

        MessageBoxResult odpowiedz = MessageBox.Show(
            $"Na liście jest {Lista.Children.Count} zapisów. Na pewno zamknąć program?",
            "Zamykanie", MessageBoxButton.YesNo, MessageBoxImage.Question);

        if (odpowiedz == MessageBoxResult.No)
        {
            e.Cancel = true;     // anuluje zamykanie okna
        }
    }
}

Co dzieje się po kolei

  • Konstruktor wywołuje PokazMiejsca(), więc napis „Wolne miejsca: 12 z 12” jest widoczny od startu.
  • Kliknięcie Zapisz (albo Enter — IsDefault) sprawdza dane po kolei. Przy pierwszym problemie pojawia się komunikat z ikoną dobraną do sytuacji, kursor wraca do pola, a return kończy metodę.
  • Trzecie sprawdzenie dotyczy reguły, a nie formatu danych: liczba osób jest poprawna, ale nie mieści się w wolnych miejscach.
  • Pytanie o potwierdzenie zatrzymuje metodę w miejscu MessageBox.Show. Kod idzie dalej dopiero po kliknięciu Tak albo Nie.
  • Przy Nie (albo zamknięciu pytania krzyżykiem) metoda się kończy — nic nie zostaje zapisane, a pola zachowują wpisane dane.
  • Po zapisie nowy napis trafia do panelu Lista, numer to liczba dotychczasowych wpisów + 1, a pola są czyszczone.
  • Window_Closing pyta o zamknięcie tylko wtedy, gdy na liście są zapisy. e.Cancel = true zatrzymuje zamykanie. Klasa CancelEventArgs wymaga using System.ComponentModel;.

Sprawdź trzy rzeczy

  1. Kliknij Zapisz, a gdy pojawi się pytanie, spróbuj kliknąć pole tekstowe w głównym oknie. Nie da się — komunikat jest modalny.
  2. Zmień MessageBoxButton.YesNo w pytaniu o zapis na OKCancel. Zapis przestanie działać — warunek sprawdza Yes, a teraz przychodzi OK.
  3. Zamknij okno przy pustej liście i przy liście z jednym zapisem. Pytanie pojawi się tylko w drugim przypadku.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementZnaczenieUwagi
MessageBox.Show(tekst, tytuł, przyciski, ikona)okno komunikatuModalne — kod czeka na zamknięcie.
MessageBoxButtonzestaw przyciskówOK, OKCancel, YesNo, YesNoCancel.
MessageBoxImageikonaInformation, Warning, Error, Question.
MessageBoxResultkliknięty przyciskYes, No, OK, Cancel.
Closingzdarzenie okna przed zamknięciemPodpinane w znaczniku <Window>.
CancelEventArgs e, e.Cancel = trueanulowanie zamykaniaWymaga using System.ComponentModel;.
Close()zamknięcie okna z koduTeż wywołuje Closing.
CZĘSTE BŁĘDY

Zanim utkniesz

Zapis lub sytuacjaProblem
MessageBox.Show("Błąd", MessageBoxImage.Error);Brak takiej wersji metody — ikona wymaga też tytułu i przycisków, w tej kolejności.
if (MessageBox.Show(…) == "Yes")CS0019 — wynik to MessageBoxResult, nie tekst. Porównaj z MessageBoxResult.Yes.
przyciski OKCancel, a sprawdzane YesWarunek nigdy nie jest spełniony — zestaw przycisków i sprawdzana wartość muszą do siebie pasować.
MessageBox w TextChangedKomunikat po każdym naciśnięciu klawisza — nie da się pisać.
Window_Closing(object sender, RoutedEventArgs e)CS0123 — Closing wymaga parametru CancelEventArgs.
brak using System.ComponentModel;CS0246 — nie znaleziono CancelEventArgs.
Close() wewnątrz Window_ClosingOkno już się zamyka — ponowne Close() w trakcie zamykania kończy się wyjątkiem InvalidOperationException.
polskie tytuły z literówkami, inne niż w arkuszuNa egzaminie treść komunikatu bywa oceniana — przepisuj ją dokładnie.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Kalkulator dzielenia★☆☆

Dwa pola na liczby i przycisk Podziel. Wynik (z dwoma miejscami po przecinku) pokazuj w napisie w oknie. Błędy zgłaszaj okienkiem: brak lub błędna liczba — Error z tytułem „Błędne dane”, dzielenie przez zero — Warning z tytułem „Dzielenie przez zero” i treścią „Nie można dzielić przez 0.”. Po zamknięciu komunikatu kursor ma trafić do pola z błędem.

pokaż wskazówkę
  • Kolejność: najpierw sprawdź oba pola TryParse, potem dzielnik.
  • Focus() wywołaj po MessageBox.Show — kod i tak poczeka na zamknięcie komunikatu.
ZAD 2Notatki z potwierdzeniem usunięcia★☆☆

Pole, przycisk Dodaj notatkę i lista notatek pod spodem (napisy w panelu). Przycisk Usuń wszystkie pyta: „Usunąć 5 notatek? Tej operacji nie da się cofnąć.” (z liczbą notatek) z ikoną ostrzeżenia. Przy pustej liście zamiast pytania pokaż informację „Lista jest pusta.”. Po usunięciu — informacja „Usunięto 5 notatek.”.

pokaż wskazówkę
  • Liczbę notatek zapamiętaj w zmiennej przed czyszczeniem panelu — po Clear() Count będzie równe 0.
ZAD 3Szybki test z informatyki★★☆

Okno z trzema pytaniami i polem na odpowiedź pod każdym, np. „Ile bitów ma bajt?”, „Jak nazywa się język opisu wyglądu w WPF?”, „Ile wynosi 2 do potęgi 10?”. Przycisk Sprawdź liczy poprawne odpowiedzi (wielkość liter i spacje na brzegach nie mają znaczenia) i pokazuje okienko z wynikiem „Wynik: 2/3”. Ikona: Information przy 3/3, Warning przy 1–2, Error przy 0. Potem pyta „Spróbować jeszcze raz?” — przy Tak czyści pola, przy Nie blokuje pola i przycisk.

pokaż wskazówkę
  • Porównanie bez wielkości liter: zamień obie strony na małe litery (ToLower()) po Trim().
  • Ikonę wybierz do zmiennej typu MessageBoxImage, a potem użyj jej w jednym wywołaniu Show.
ZAD 4Edytor z niezapisanymi zmianami★★☆

Wielowierszowe pole notatki i przycisk Zapisz (na razie zapis tylko „udaje”: ustawia napis „Zapisano o 12:05”). W tytule okna pojawia się gwiazdka, gdy są niezapisane zmiany: „Notatka*”. Przy zamykaniu okna z niezapisanymi zmianami program pyta „Zapisać zmiany przed zamknięciem?” z przyciskami Tak / Nie / Anuluj: Tak — „zapisuje” i zamyka, Nie — zamyka bez zapisu, Anuluj — okno zostaje otwarte.

pokaż wskazówkę
  • Pole bool zmieniono ustawiaj w TextChanged, zeruj przy zapisie.
  • W Closing obsłuż trzy wyniki YesNoCancel; tylko jeden z nich ustawia e.Cancel = true.
ZAD 5Logowanie z trzema próbami★★☆

Okno logowania do panelu biblioteki: pola Login i Kod PIN (na razie zwykłe pole tekstowe — pole na hasło poznasz w lekcji 18) oraz przycisk Zaloguj. Poprawne dane zapisz w stałych. Błędne dane: ostrzeżenie „Błędny login lub PIN. Pozostało prób: 2”, czyszczenie pola PIN i kursor w nim. Po trzeciej nieudanej próbie: błąd „Konto zablokowane. Skontaktuj się z bibliotekarzem.” i zamknięcie programu. Po udanym logowaniu: informacja „Witaj, …!” i ukrycie formularza, a pokazanie (wcześniej zwiniętego) napisu „Panel biblioteki”.

pokaż wskazówkę
  • Licznik prób — pole klasy. Zamknięcie programu z kodu — Close().
  • W komunikacie nie zdradzaj, czy błędny był login, czy PIN — to dobra praktyka bezpieczeństwa.
ZAD 6Kasa sklepiku szkolnego★★★

Zbuduj kasę: rząd przycisków produktów z ceną w Tag (np. Drożdżówka 3,50 zł, Woda 2,50 zł, Kanapka 6,00 zł, Sok 4,00 zł). Kliknięcie dopisuje pozycję do paragonu (panel z napisami) i aktualizuje „Razem: 13,50 zł”. Pole „Kwota od klienta” i przycisk Zapłać:

  • pusty paragon — informacja „Paragon jest pusty.”;
  • błędna kwota — błąd; kwota za mała — ostrzeżenie „Brakuje 2,50 zł.”;
  • poprawna — pytanie „Do zapłaty 13,50 zł. Otrzymano 20,00 zł. Reszta: 6,50 zł. Zatwierdzić?”; po Tak paragon się czyści, a licznik „Transakcji dziś: 4” rośnie.

Przycisk Anuluj paragon pyta o potwierdzenie. Przy zamykaniu programu z niepustym paragonem — ostrzeżenie z pytaniem.

pokaż wskazówkę
  • Kwoty trzymaj w decimal; ceny z Tag zamieniaj przez decimal.Parse.
  • Sumę paragonu trzymaj w polu klasy, zerowanym przy zapłacie i anulowaniu.
PODSUMOWANIE

Co trzeba zapamiętać

  • MessageBox.Show(tekst, tytuł, przyciski, ikona) — komunikat modalny; kod czeka, aż użytkownik go zamknie.
  • Ikona mówi, jak poważna jest sytuacja: Information, Warning, Error, Question.
  • Odpowiedź to MessageBoxResult — porównuj ją z wartością pasującą do zestawu przycisków.
  • Pytaj o potwierdzenie przed operacjami nieodwracalnymi; zwykłe wyniki pokazuj w oknie.
  • Closing + e.Cancel = true zatrzymuje zamykanie okna; Close() zamyka okno z kodu.

Dokumentacja: Microsoft Learn — klasa MessageBox, zdarzenie Window.Closing.

Postęp zapisuje się w tej przeglądarce.