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

PasswordBox — pole hasła

Okno logowania i rejestracji to klasyk egzaminu INF.04. Hasło wpisuje się w specjalne pole, które zamiast liter pokazuje kropki i nie pozwala skopiować zawartości. W tej lekcji poznasz PasswordBox, zbudujesz wskaźnik siły hasła z listą wymagań, sprawdzisz zgodność dwóch haseł i dodasz przełącznik „Pokaż hasło”.

XAML C# PasswordBox Password PasswordChanged char.IsDigit 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Dodasz pole hasła i odczytasz z niego tekst przez Password.
  • Zmienisz znak maskujący i ograniczysz długość hasła.
  • Zareagujesz na każdą zmianę hasła zdarzeniem PasswordChanged.
  • Sprawdzisz, czy hasło zawiera wielką literę, cyfrę i znak specjalny (char.IsUpper, char.IsDigit…).
  • Porównasz dwa hasła i dodasz przełącznik „Pokaż hasło”.
  • Wyjaśnisz, dlaczego prawdziwe programy nie przechowują haseł jawnym tekstem.
TEORIA

PasswordBox

MainWindow.xaml
<StackPanel Margin="15" Width="240" HorizontalAlignment="Left">
    <Label Content="_Login:" Target="{Binding ElementName=Login}"/>
    <TextBox x:Name="Login"/>
    <Label Content="_Hasło:" Target="{Binding ElementName=Haslo}"/>
    <PasswordBox x:Name="Haslo" MaxLength="32"/>
    <Button Content="Zaloguj" Margin="0,10,0,0" IsDefault="True" Click="Zaloguj_Click"/>
</StackPanel>
MainWindow.xaml.cs
private void Zaloguj_Click(object sender, RoutedEventArgs e)
{
    if (Login.Text == "admin" && Haslo.Password == "Szkola2026!")
    {
        MessageBox.Show("Zalogowano.");
    }
    else
    {
        MessageBox.Show("Błędny login lub hasło.", "Logowanie",
                        MessageBoxButton.OK, MessageBoxImage.Error);
        Haslo.Clear();
        Haslo.Focus();
    }
}
Logowanie–□✕
Login:
admin
Hasło:
●●●●●●●●●●●
Zaloguj

pole hasła pokazuje kropki — Ctrl + C nic z niego nie skopiuje

TextBoxPasswordBox
TreśćTextPassword
Wyświetlaniejawny tekstznaki maskujące (PasswordChar, domyślnie ●)
Kopiowanietaknie
Zdarzenie zmianyTextChangedPasswordChanged
Wiele linii, IsReadOnlytaknie

Znak maskujący zmienisz atrybutem PasswordChar="*". MaxLength, Clear(), Focus() i SelectAll() działają jak w polu tekstowym.

TEORIA

Zdarzenie PasswordChanged

Wywołuje się po każdej zmianie hasła — wpisaniu, usunięciu znaku, wklejeniu i zmianie z kodu. W odróżnieniu od TextChanged parametr ma zwykły typ RoutedEventArgs:

MainWindow.xaml.cs
// w XAML: <PasswordBox x:Name="Haslo" PasswordChanged="Haslo_PasswordChanged"/>
private void Haslo_PasswordChanged(object sender, RoutedEventArgs e)
{
    Dlugosc.Text = $"Znaków: {Haslo.Password.Length}";
}
TEORIA

Sprawdzanie znaków

Hasło to napis, więc możesz przejść po nim pętlą znak po znaku. Typ char ma gotowe metody do rozpoznawania rodzaju znaku:

MetodaZwraca true, gdy znak to…
char.IsUpper(z)wielka litera (także Ą, Ł, Ż)
char.IsLower(z)mała litera
char.IsDigit(z)cyfra 0–9
char.IsLetter(z)dowolna litera
char.IsLetterOrDigit(z)litera albo cyfra — zaprzeczenie to znak specjalny lub spacja
char.IsWhiteSpace(z)spacja, tabulator, nowa linia
MainWindow.xaml.cs
bool maCyfre = false;
bool maWielka = false;

foreach (char z in Haslo.Password)
{
    if (char.IsDigit(z)) maCyfre = true;
    if (char.IsUpper(z)) maWielka = true;
}

if (Haslo.Password.Length < 8 || !maCyfre || !maWielka)
{
    Info.Text = "Hasło: min. 8 znaków, wielka litera i cyfra.";
}
TEORIA

Pokaż hasło

PasswordBox nie umie pokazać hasła. Robi się to sztuczką: w tej samej komórce Grid stawiasz PasswordBox i zwykły TextBox z Visibility="Collapsed". Pole wyboru „Pokaż hasło” przepisuje tekst do widocznego pola i zamienia widoczność. Ważne, żeby jedno z pól było „źródłem prawdy” — w przykładzie jest to PasswordBox, a jawne pole przy każdej zmianie przepisuje do niego swój tekst.

TEORIA

Hasło w programie

W ćwiczeniach porównujesz hasło ze stałą zapisaną w kodzie — tak jest na egzaminie i tak się uczy. Pamiętaj jednak, że w prawdziwych aplikacjach:

  • hasła nigdy nie są zapisane jawnie w kodzie ani w bazie — przechowuje się ich skrót (hash), z którego nie da się odtworzyć hasła;
  • komunikat po nieudanym logowaniu nie mówi, czy błędny był login, czy hasło — „Błędny login lub hasło” utrudnia zgadywanie kont;
  • po kilku nieudanych próbach logowanie jest blokowane na pewien czas.

Dlatego też PasswordBox celowo nie pozwala kopiować zawartości i nie współpracuje z mechanizmami, które poznasz w bloku o danych.

PRZYKŁAD

Zakładanie konta z wskaźnikiem siły hasła

Formularz zakładania konta w e-dzienniku. Podczas wpisywania hasła na żywo zmieniają się: cztery paski siły, opis („Hasło dobre”) i lista pięciu wymagań z ✓ lub ✗. Pod drugim polem widać, czy hasła są zgodne. Przycisk Załóż konto włącza się przy loginie od 3 znaków, haśle co najmniej „dobrym” (4 z 5 wymagań) i zgodnych hasłach.

Konto w e-dzienniku–□✕
Login:
ola.kowalska
Hasło:
●●●●●●●●●●
Pokaż hasło
Hasło dobre
✓ co najmniej 8 znaków
✓ wielka litera
✓ mała litera
✓ cyfra
✗ znak specjalny, np. ! ? # @
Powtórz hasło:
●●●●●●●●●●
Hasła są zgodne
Załóż konto

hasło „Lebork2026” — 4 z 5 wymagań, brak znaku specjalnego

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja18.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Konto w e-dzienniku" SizeToContent="Height" Width="460"
        ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">

    <Grid Margin="15">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Content="_Login:" Target="{Binding ElementName=Login}"/>
        <TextBox x:Name="Login" Grid.Row="0" Grid.Column="1" Margin="0,4" MaxLength="20"
                 TextChanged="Login_TextChanged"/>

        <Label Grid.Row="1" Content="_Hasło:" Target="{Binding ElementName=Haslo}"/>
        <!-- PasswordBox i zwykłe pole w tej samej komórce: widoczne jest jedno z nich -->
        <PasswordBox x:Name="Haslo" Grid.Row="1" Grid.Column="1" Margin="0,4"
                     MaxLength="32" PasswordChanged="Haslo_PasswordChanged"/>
        <TextBox x:Name="HasloJawne" Grid.Row="1" Grid.Column="1" Margin="0,4"
                 MaxLength="32" Visibility="Collapsed" TextChanged="HasloJawne_TextChanged"/>

        <CheckBox x:Name="Pokaz" Grid.Row="2" Grid.Column="1" Content="Pokaż hasło"
                  Margin="0,2,0,6" Checked="Pokaz_Zmiana" Unchecked="Pokaz_Zmiana"/>

        <!-- wskaźnik siły: 4 paski -->
        <StackPanel Grid.Row="3" Grid.Column="1" Margin="0,0,0,8">
            <UniformGrid x:Name="Paski" Columns="4" Height="8">
                <Border Margin="0,0,3,0" CornerRadius="2" Background="#FFE5E7EB"/>
                <Border Margin="0,0,3,0" CornerRadius="2" Background="#FFE5E7EB"/>
                <Border Margin="0,0,3,0" CornerRadius="2" Background="#FFE5E7EB"/>
                <Border CornerRadius="2" Background="#FFE5E7EB"/>
            </UniformGrid>
            <TextBlock x:Name="Ocena" Margin="0,3,0,0" FontWeight="Bold" Text="Wpisz hasło"/>
        </StackPanel>

        <!-- lista wymagań: każdy napis zamienia się na ✓ albo ✗ -->
        <StackPanel x:Name="Wymagania" Grid.Row="4" Grid.Column="1" Margin="0,0,0,8">
            <TextBlock Text="✗ co najmniej 8 znaków"/>
            <TextBlock Text="✗ wielka litera"/>
            <TextBlock Text="✗ mała litera"/>
            <TextBlock Text="✗ cyfra"/>
            <TextBlock Text="✗ znak specjalny, np. ! ? # @"/>
        </StackPanel>

        <Label Grid.Row="5" Content="_Powtórz hasło:" Target="{Binding ElementName=Powtorz}"/>
        <StackPanel Grid.Row="5" Grid.Column="1">
            <PasswordBox x:Name="Powtorz" Margin="0,4" MaxLength="32"
                         PasswordChanged="Powtorz_PasswordChanged"/>
            <TextBlock x:Name="Zgodnosc" FontSize="11"/>
        </StackPanel>

        <Button x:Name="Zaloz" Grid.Row="6" Grid.Column="1" Content="Załóż konto"
                HorizontalAlignment="Right" Width="120" Height="30" Margin="0,12,0,0"
                IsEnabled="False" IsDefault="True" Click="Zaloz_Click"/>
    </Grid>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Lekcja18;

public partial class MainWindow : Window
{
    private readonly string[] opisy = { "bardzo słabe", "słabe", "średnie", "dobre", "silne", "bardzo silne" };
    private int sila = 0;      // ile z 5 wymagań spełnia hasło

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Haslo_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Sprawdz();
    }

    private void Powtorz_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Sprawdz();
    }

    private void Login_TextChanged(object sender, TextChangedEventArgs e)
    {
        Sprawdz();
    }

    // Jawne pole przepisuje tekst do PasswordBox — ono jest „źródłem prawdy”
    private void HasloJawne_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (HasloJawne.Visibility == Visibility.Visible)
        {
            Haslo.Password = HasloJawne.Text;
        }
    }

    private void Pokaz_Zmiana(object sender, RoutedEventArgs e)
    {
        if (Pokaz.IsChecked == true)
        {
            HasloJawne.Text = Haslo.Password;
            Haslo.Visibility = Visibility.Collapsed;
            HasloJawne.Visibility = Visibility.Visible;
        }
        else
        {
            HasloJawne.Visibility = Visibility.Collapsed;
            Haslo.Visibility = Visibility.Visible;
        }
    }

    private void Zaloz_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show($"Konto „{Login.Text.Trim()}” zostało utworzone.\nSiła hasła: {opisy[sila]}.",
                        "E-dziennik", MessageBoxButton.OK, MessageBoxImage.Information);

        Login.Clear();
        Haslo.Clear();
        Powtorz.Clear();
        Pokaz.IsChecked = false;
        Login.Focus();
    }

    private void Sprawdz()
    {
        // zdarzenia mogą przyjść, zanim powstaną kontrolki niżej w XAML
        if (Zaloz == null)
        {
            return;
        }

        string haslo = Haslo.Password;

        // 1. Pięć wymagań
        bool[] spelnione = new bool[5];
        spelnione[0] = haslo.Length >= 8;
        foreach (char z in haslo)
        {
            if (char.IsUpper(z)) spelnione[1] = true;
            if (char.IsLower(z)) spelnione[2] = true;
            if (char.IsDigit(z)) spelnione[3] = true;
            if (!char.IsLetterOrDigit(z)) spelnione[4] = true;
        }

        sila = 0;
        for (int i = 0; i < 5; i++)
        {
            TextBlock wiersz = (TextBlock)Wymagania.Children[i];
            string opis = wiersz.Text.Substring(2);           // tekst bez „✓ ” / „✗ ”
            wiersz.Text = (spelnione[i] ? "✓ " : "✗ ") + opis;
            wiersz.Foreground = spelnione[i] ? Brushes.Green : Brushes.Gray;
            if (spelnione[i])
            {
                sila++;
            }
        }

        // 2. Paski i opis siły
        Brush kolor = sila <= 1 ? Brushes.Red : sila <= 3 ? Brushes.Orange : Brushes.Green;
        int paskow = haslo.Length == 0 ? 0 : (sila + 1) * 4 / 6;   // 0–5 → 0–4 paski
        for (int i = 0; i < 4; i++)
        {
            Border pasek = (Border)Paski.Children[i];
            pasek.Background = i < paskow ? kolor : Brushes.Gainsboro;
        }
        Ocena.Text = haslo.Length == 0 ? "Wpisz hasło" : $"Hasło {opisy[sila]}";
        Ocena.Foreground = haslo.Length == 0 ? Brushes.Black : kolor;

        // 3. Zgodność haseł
        bool zgodne = Powtorz.Password != "" && Powtorz.Password == haslo;
        if (Powtorz.Password == "")
        {
            Zgodnosc.Text = "";
        }
        else
        {
            Zgodnosc.Text = zgodne ? "Hasła są zgodne" : "Hasła się różnią";
            Zgodnosc.Foreground = zgodne ? Brushes.Green : Brushes.Red;
        }

        // 4. Przycisk: login min. 3 znaki, hasło co najmniej „dobre”, hasła zgodne
        Zaloz.IsEnabled = Login.Text.Trim().Length >= 3 && sila >= 4 && zgodne;
    }
}

Co dzieje się po kolei

  • Okno ma SizeToContent="Height" (lekcja 10) — wszystkie wiersze siatki są Auto, więc wysokość dopasowuje się do zawartości.
  • PasswordBox i ukryty TextBox stoją w tej samej komórce. „Pokaż hasło” zamienia ich widoczność i przy pokazywaniu przepisuje hasło do jawnego pola.
  • Wszystkie zdarzenia — zmiana loginu, hasła, powtórzenia — wywołują jedną metodę Sprawdz, która liczy wszystko od nowa.
  • Wymagania są zapisane w tablicy bool[5]. Pierwsze to długość, cztery kolejne ustawia jedna pętla po znakach hasła.
  • Napisy wymagań stoją w panelu Wymagania w tej samej kolejności co elementy tablicy, więc Children[i] odpowiada spelnione[i]. Substring(2) odcina dotychczasowy znak i spację.
  • Operator ? : wybiera kolor, symbol i opis bez długich if. Liczba zielonych pasków: (sila + 1) * 4 / 6 zamienia 0–5 spełnionych wymagań na 0–4 paski (dzielenie całkowite).
  • Hasła są zgodne tylko wtedy, gdy powtórzenie nie jest puste i jest identyczne — porównanie == uwzględnia wielkość liter.
  • Po założeniu konta Clear() czyści pola. Każde czyszczenie wywołuje zdarzenie zmiany, więc wskaźnik i przycisk same wracają do stanu początkowego.

Sprawdź trzy rzeczy

  1. Wpisz hasło i spróbuj skopiować je skrótem Ctrl + C, a potem wkleić do pola loginu. Nic się nie wklei.
  2. Wpisz hasło „Zażółć1!” (8 znaków). Polskie litery też są rozpoznawane jako wielkie i małe.
  3. Dodaj do PasswordBox atrybut PasswordChar="*". Kropki zamienią się na gwiazdki.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTypCo robi
PasswordstringWpisane hasło; zapis z kodu też działa.
PasswordCharcharZnak maskujący, domyślnie ●.
MaxLengthintMaksymalna długość hasła.
PasswordChangedzdarzeniePo każdej zmianie; parametr RoutedEventArgs.
Clear()metodaCzyści pole.
char.IsUpper, IsLower, IsDigit, IsLetterOrDigitboolRodzaj znaku.
warunek ? a : boperatorZwraca a, gdy warunek prawdziwy, w przeciwnym razie b.
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
Haslo.TextCS1061 — PasswordBox nie ma Text. Użyj Password.
PasswordChanged z parametrem TextChangedEventArgsBłąd kompilacji — parametr to RoutedEventArgs.
TextChanged="…" na PasswordBoxBłąd XAML — takiego zdarzenia nie ma.
PasswordChar="**"Błąd — to jeden znak.
komunikat „Błędne hasło dla użytkownika admin”Zdradza, że taki login istnieje. Pisz „Błędny login lub hasło”.
porównanie haseł przez ToLower()Hasła rozróżniają wielkość liter — tak ma zostać.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Komunikaty o błędnym logowaniu nie mogą zdradzać, która część danych była błędna. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Sejf hotelowy★☆☆

Sejf w pokoju hotelowym. Gdy jest otwarty, gość ustawia nowy 4-cyfrowy PIN (dwa pola hasła ze znakiem *, PIN musi mieć dokładnie 4 cyfry, oba pola zgodne) i klika Zamknij sejf. Napis stanu zmienia się na czerwone „ZAMKNIĘTY”. Żeby otworzyć, trzeba wpisać PIN w trzecie pole i kliknąć Otwórz; zielone „OTWARTY” pozwala ustawić nowy PIN. Pola nieprzydatne w danym stanie są ukryte.

pokaż wskazówkę
  • Elementy stanu „otwarty” i „zamknięty” połóż w dwóch panelach i przełączaj ich Visibility.
  • „Same cyfry” sprawdzisz pętlą z char.IsDigit.
ZAD 2Wybór znaku maskującego★☆☆

Pole hasła, pod nim licznik „Długość: 7 / 20” i grupa przycisków opcji „Znak maskujący”: ●, *, #, ✱. Wybór opcji od razu zmienia wygląd pola. Licznik robi się czerwony przy hasłach krótszych niż 8 znaków. Przycisk Wyczyść czyści pole i ustawia w nim kursor.

pokaż wskazówkę
  • Znak możesz trzymać w Content przycisku opcji; PasswordChar ma typ char, więc weź pierwszy znak napisu: $"{opcja.Content}"[0].
ZAD 3Zmiana hasła★★☆

Okno zmiany hasła użytkownika „jan.nowak”: pola Obecne hasło (poprawne zapisz jako stałą), Nowe hasło, Powtórz nowe. Po kliknięciu Zmień program sprawdza wszystkie zasady i wypisuje wszystkie problemy naraz w czerwonej ramce, każdy w nowej linii: błędne obecne hasło, nowe takie samo jak obecne, krótsze niż 10 znaków, bez cyfry, zawiera login (bez względu na wielkość liter), hasła się różnią. Gdy wszystko jest dobrze — zielona ramka i nowe hasło staje się obecnym.

pokaż wskazówkę
  • Zbieraj problemy w zmiennej string bledy, dopisując kolejne linie; na końcu sprawdź, czy jest pusta.
  • Obecne hasło trzymaj w polu klasy (nie w stałej), skoro ma się zmieniać.
ZAD 4Ile czasu na złamanie?★★☆

Program szacuje, ile czasu zajęłoby odgadnięcie hasła metodą „sprawdzam wszystkie kombinacje” przy 10 miliardach prób na sekundę. Liczba możliwych znaków zależy od tego, co jest w haśle: małe litery 26, wielkie +26, cyfry +10, znaki specjalne +32. Liczba kombinacji = znakidługość. Wynik na żywo w czytelnej jednostce: „mniej niż sekunda”, „14 minut”, „3 dni”, „2 400 lat”. Dodatkowo, jeśli hasło (bez względu na wielkość liter) jest na liście 10 najpopularniejszych haseł zapisanej w tablicy (np. „123456”, „qwerty”, „haslo”), wynik to „natychmiast — hasło jest na liście najpopularniejszych!”.

pokaż wskazówkę
  • Licz na double: Math.Pow(znaki, dlugosc) / 1e10 daje sekundy. Potem dziel przez 60, 3600, 86400, 31 536 000 i wybierz jednostkę w if.
  • Format z odstępami tysięcy: {wartosc:N0}.
ZAD 5Konto w bibliotece szkolnej★★☆

Formularz rejestracji czytelnika: imię i nazwisko, klasa (lista rozwijana), e-mail, hasło i powtórzenie, pole wyboru akceptacji regulaminu. Przycisk Zarejestruj jest aktywny tylko wtedy, gdy wszystkie pola są wypełnione, e-mail zawiera @ i kropkę po nim, hasło ma min. 8 znaków z cyfrą, hasła są zgodne, a regulamin zaakceptowany. Pod przyciskiem mała szara podpowiedź, czego jeszcze brakuje (pierwszy niespełniony warunek). Po rejestracji MessageBox pokazuje login w postaci „imie.nazwisko.3ti” (małe litery, bez polskich znaków).

pokaż wskazówkę
  • Jedna metoda „sprawdź formularz” wywoływana ze wszystkich zdarzeń zmian: TextChanged, PasswordChanged, SelectionChanged, Checked/Unchecked.
  • Polskie znaki zamienisz tak jak w zadaniu z konwerterem tekstu z lekcji 13.
ZAD 6Mini-menedżer haseł★★★

Po uruchomieniu widać tylko pole hasła głównego. Po wpisaniu poprawnego (stała) pokazuje się właściwa część okna: lista serwisów (np. e-dziennik, poczta, Steam) i obok szczegóły zaznaczonego: login i hasło zamaskowane jako ●●●●●●●●. Przycisk Pokaż odsłania hasło, a jego napis zmienia się na Ukryj. Formularz Dodaj (serwis, login, hasło) dopisuje wpis — najwyżej 10 wpisów. Zablokuj wraca do ekranu hasła głównego i czyści wszystkie pola. Po trzech błędnych hasłach głównych program się zamyka.

pokaż wskazówkę
  • Dane trzymaj w trzech tablicach po 10 elementów i licznik wpisów — numer na liście = numer w tablicach.
  • Dwie „strony” okna to dwa panele w jednej komórce Grid, przełączane przez Visibility.
  • Zamaskowane hasło: new string('●', haslo.Length).
PODSUMOWANIE

Co trzeba zapamiętać

  • PasswordBox — hasło w Password (nie Text), maskowane znakiem PasswordChar, bez kopiowania.
  • PasswordChanged reaguje na każdą zmianę; parametr to RoutedEventArgs.
  • char.IsUpper, IsLower, IsDigit, IsLetterOrDigit w pętli po znakach sprawdzają skład hasła.
  • „Pokaż hasło” = PasswordBox i TextBox w jednej komórce + przełączanie widoczności.
  • Komunikat logowania nie zdradza, czy błędny był login, czy hasło. Prawdziwe systemy przechowują skróty haseł, nie hasła.

Dokumentacja: Microsoft Learn — klasa PasswordBox.

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