Aplikacje desktopowe · Okna

Okna i dialogi: modalne, niemodalne i MessageBox

Prawdziwe aplikacje rzadko mają jedno okno. Potrzebne są potwierdzenia, ustawienia i formularze szczegółów. Zobaczysz, czym różni się ShowDialog() od Show(), kiedy program czeka, a kiedy leci dalej, i jak przekazać dane między oknami.

  • około 45 minut
  • WPF · ShowDialog · MessageBox
  • INF.04.5.3

Po tej lekcji: otworzyć okno modalne i niemodalne, odczytać wynik dialogu (DialogResult), użyć MessageBox z przyciskami Tak i Nie oraz przekazać dane do drugiego okna i z powrotem.

Zacznijmy od edytora

„Czy zapisać zmiany?”

Gdy zamykasz dokument z niezapisanymi zmianami, pojawia się okno z pytaniem. Dopóki nie odpowiesz, nie możesz kliknąć niczego innego w programie. To okno modalne.

Panel „Znajdź i zamień” w wielu edytorach zachowuje się inaczej: możesz przełączać się między nim a dokumentem, nie zamykając go. To okno niemodalne.

Znajomość tej różnicy to jedno z najczęściej sprawdzanych zagadnień w zadaniach egzaminacyjnych z aplikacją wielookienkową.

1 · Poznaj narzędzia

Okno modalne, niemodalne i MessageBox

Okno modalneShowDialog()

Blokuje resztę aplikacji, dopóki go nie zamkniesz. Program czeka w linii z ShowDialog() i wraca do niej dopiero po zamknięciu okna.

Na przykład formularz edycji rekordu

Okno niemodalneShow()

Oba okna działają niezależnie. Program nie czeka, wykonanie leci dalej natychmiast.

Na przykład panel narzędzi, okno pomocy

MessageBoxgotowy komunikat

Wbudowane okno modalne do prostych komunikatów i pytań. Nie projektujesz go sam, a wynik sprawdzasz przez MessageBoxResult.

Na przykład „Czy na pewno usunąć?”

Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji

2 · Sprawdź w działaniu

Kiedy program czeka, a kiedy leci dalej

Kliknij „Edytuj” i zobacz, że okno główne się blokuje. Zmień imię i kliknij Zapisz. Potem przełącz sposób otwarcia na Show() i powtórz: tym razem okno główne działa, ale nowe imię się nie pojawia. Wypróbuj też „Usuń” i „Pomoc”.

Ta interakcja wymaga włączonego JavaScriptu.

Jeśli potrzebujesz wyniku z okna (zapisano czy anulowano, co wpisano), użyj ShowDialog(). Show() nie zaczeka na użytkownika.

3 · Kod okna głównego

MainWindow.xaml.cs

Trzy przyciski, trzy techniki. Zauważ, kiedy odczytujesz dane z okna: tylko po ShowDialog().

MainWindow.xaml.cscsharp
using System.Windows;

namespace OknaApp;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void PrzyciskEdytuj_Click(object sender, RoutedEventArgs e)
    {
        OknoEdycji okno = new OknoEdycji();
        okno.ImieDoEdycji = EtykietaImie.Text;   // przekazanie danych DO okna

        bool? wynik = okno.ShowDialog();         // program CZEKA tutaj, aż użytkownik zamknie okno

        if (wynik == true)                       // użytkownik kliknął „Zapisz”
        {
            EtykietaImie.Text = okno.ImieDoEdycji;   // odczyt danych PO zamknięciu
        }
    }

    private void PrzyciskUsun_Click(object sender, RoutedEventArgs e)
    {
        MessageBoxResult wynik = MessageBox.Show(
            "Czy na pewno chcesz usunąć ten rekord?",
            "Potwierdzenie",
            MessageBoxButton.YesNo,
            MessageBoxImage.Warning);

        if (wynik == MessageBoxResult.Yes)
        {
            EtykietaImie.Text = "(usunięto)";
        }
    }

    private void PrzyciskPomoc_Click(object sender, RoutedEventArgs e)
    {
        OknoPomocy okno = new OknoPomocy();
        okno.Show();                             // niemodalne: można dalej używać okna głównego
    }
}
  1. okno.ImieDoEdycji = EtykietaImie.Text Przekazanie danych do okna przed jego pokazaniem.
  2. bool? wynik = okno.ShowDialog() Program czeka tutaj. Wynik to true, false albo null.
  3. if (wynik == true) Zapis tylko wtedy, gdy użytkownik zatwierdził okno przyciskiem Zapisz.
  4. MessageBox.Show(…, YesNo, Warning) Pytanie z dwoma przyciskami i ikoną ostrzeżenia. Zwraca MessageBoxResult.
  5. okno.Show() Okno pomocy działa równolegle z głównym.

4 · Kod drugiego okna

OknoEdycji.xaml.cs

Okno dialogowe udostępnia publiczną właściwość, przez którą wymienia dane z oknem głównym, i ustawia DialogResult.

OknoEdycji.xaml.cscsharp
using System.Windows;

namespace OknaApp;

public partial class OknoEdycji : Window
{
    public string ImieDoEdycji
    {
        get => PoleImie.Text;
        set => PoleImie.Text = value;
    }

    public OknoEdycji()
    {
        InitializeComponent();
    }

    private void Zapisz_Click(object sender, RoutedEventArgs e)
    {
        DialogResult = true;   // zamyka okno i zwraca true do ShowDialog()
    }
}
  1. public string ImieDoEdycji Most do danych: okno główne ustawia ją przed pokazaniem i odczytuje po zamknięciu.
  2. DialogResult = true Zamyka okno i zwraca true do ShowDialog().
  3. IsCancel="True" na przycisku Anuluj W XAML ten atrybut sam zamyka okno z wynikiem false, więc przycisk nie potrzebuje kodu. IsDefault="True" na Zapisz reaguje na Enter.

Do zadań

Lista, błąd bez zamykania okna i okno statystyk

Zadania 2 i 3 wymagają trzech rzeczy: dodania wpisu do ListBox, zostawienia okna otwartego po błędzie oraz odświeżania drugiego okna. Najprościej przekazać drugiemu oknu tę samą kolekcję przez konstruktor.

MainWindow.xaml.cs i StatystykiWindow.xaml.cscsharp
// --- Okno dodawania: błędna cena nie zamyka okna ---
private void Zapisz_Click(object sender, RoutedEventArgs e)
{
    if (!double.TryParse(PoleCena.Text, out double cena))
    {
        MessageBox.Show("Cena musi być liczbą.", "Błąd", MessageBoxButton.OK, MessageBoxImage.Warning);
        return;                    // wychodzimy, DialogResult nie zostaje ustawiony
    }
    Nazwa = PoleNazwa.Text;
    Cena = cena;
    DialogResult = true;           // dopiero to zamyka okno
}

// --- Okno główne: dopisanie wpisu do ListBox ---
Lista.Items.Add($"{okno.Nazwa} – {okno.Cena:0.00} zł");

// --- Okno główne: statystyki dostają tę samą kolekcję ---
private readonly ObservableCollection<string> zadania = new();   // using System.Collections.ObjectModel;
new StatystykiWindow(zadania).Show();                            // Show() = niemodalne

// --- StatystykiWindow ---
public StatystykiWindow(ObservableCollection<string> lista)
{
    InitializeComponent();
    void Odswiez() => EtykietaLiczba.Text = $"Liczba zadań: {lista.Count}";
    Odswiez();
    lista.CollectionChanged += (s, e) => Odswiez();              // wersja ze zdarzeniem
}
  1. double.TryParse Próbuje zamienić tekst na liczbę i zwraca true albo false. Wynik trafia do zmiennej po słowie out.
  2. CollectionChanged Zdarzenie ObservableCollection: odpala się po każdym dodaniu i usunięciu elementu, więc okno statystyk odświeża się samo.
  3. Zamiast zdarzenia Możesz też użyć DispatcherTimer z lekcji o kontrolkach: w Tick ustaw etykietę na lista.Count.

5 · Komunikaty

Przyciski i wyniki MessageBox

`MessageBoxButton`PrzyciskiMożliwe wyniki `MessageBoxResult`
OKOKOK
OKCancelOK, AnulujOK, Cancel
YesNoTak, NieYes, No
YesNoCancelTak, Nie, AnulujYes, No, Cancel
Ikonę wybierasz przez `MessageBoxImage`: `Information`, `Warning`, `Error` albo `Question`.

6 · Czego unikać

Trzy pułapki z oknami

`Show()` tam, gdzie potrzebny jest wynik
Tak nie
okno.Show();
EtykietaImie.Text = okno.ImieDoEdycji;   // odczyt od razu

Odczytujesz starą wartość, bo program nie zaczekał na użytkownika.

Tak
if (okno.ShowDialog() == true)
{
    EtykietaImie.Text = okno.ImieDoEdycji;
}

Tylko ShowDialog() zatrzymuje wykonanie do zamknięcia okna.

Brak `DialogResult` w oknie dialogowym
Tak nie
private void Zapisz_Click(object sender, RoutedEventArgs e)
{
    Close();   // bez DialogResult
}

ShowDialog() zwraca false (albo null), więc okno główne nie zapisze zmian.

Tak
private void Zapisz_Click(object sender, RoutedEventArgs e)
{
    DialogResult = true;
}

Wynik true odróżnia „Zapisz” od „Anuluj” i zamknięcia krzyżykiem.

Wielokrotne otwarcie tego samego okna niemodalnego
Tak nie
private void PrzyciskPomoc_Click(object sender, RoutedEventArgs e)
{
    new OknoPomocy().Show();
}

Każde kliknięcie otwiera kolejne okno pomocy.

Tak
private OknoPomocy? _pomoc;

private void PrzyciskPomoc_Click(object sender, RoutedEventArgs e)
{
    if (_pomoc == null || !_pomoc.IsLoaded)
        _pomoc = new OknoPomocy();
    _pomoc.Show();
    _pomoc.Activate();
}

Zapamiętaj okno w polu klasy i otwórz je ponownie tylko wtedy, gdy zostało zamknięte.

7 · Sprawdź się

Show, ShowDialog czy MessageBox?

Cztery sytuacje z oknami.

Ta interakcja wymaga włączonego JavaScriptu.

Podsumowanie

Co warto zapamiętać

  1. Modalne (ShowDialog()): blokuje resztę, program czeka i dostaje wynik (bool?).
  2. Niemodalne (Show()): oba okna działają, program leci dalej.
  3. Dane przekazujesz przez publiczną właściwość okna: ustaw przed pokazaniem, odczytaj po ShowDialog().
  4. DialogResult = true zamyka okno i zwraca true. IsCancel zamyka z wynikiem false.
  5. MessageBox to gotowe okno modalne, a wynik porównujesz z MessageBoxResult.

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka