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”.
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.
PasswordBox
<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>
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();
}
}
pole hasła pokazuje kropki — Ctrl + C nic z niego nie skopiuje
TextBox | PasswordBox | |
|---|---|---|
| Treść | Text | Password |
| Wyświetlanie | jawny tekst | znaki maskujące (PasswordChar, domyślnie ●) |
| Kopiowanie | tak | nie |
| Zdarzenie zmiany | TextChanged | PasswordChanged |
Wiele linii, IsReadOnly | tak | nie |
Znak maskujący zmienisz atrybutem PasswordChar="*". MaxLength, Clear(), Focus() i SelectAll() działają jak w polu tekstowym.
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:
// w XAML: <PasswordBox x:Name="Haslo" PasswordChanged="Haslo_PasswordChanged"/>
private void Haslo_PasswordChanged(object sender, RoutedEventArgs e)
{
Dlugosc.Text = $"Znaków: {Haslo.Password.Length}";
}
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:
| Metoda | Zwraca 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 |
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.";
}
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.
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.
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.
hasło „Lebork2026” — 4 z 5 wymagań, brak znaku specjalnego
<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>
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. PasswordBoxi ukrytyTextBoxstoją 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
Wymaganiaw tej samej kolejności co elementy tablicy, więcChildren[i]odpowiadaspelnione[i].Substring(2)odcina dotychczasowy znak i spację. - Operator
? :wybiera kolor, symbol i opis bez długichif. Liczba zielonych pasków:(sila + 1) * 4 / 6zamienia 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
- Wpisz hasło i spróbuj skopiować je skrótem Ctrl + C, a potem wkleić do pola loginu. Nic się nie wklei.
- Wpisz hasło „Zażółć1!” (8 znaków). Polskie litery też są rozpoznawane jako wielkie i małe.
- Dodaj do
PasswordBoxatrybutPasswordChar="*". Kropki zamienią się na gwiazdki.
Zestawienie elementów
| Element | Typ | Co robi |
|---|---|---|
Password | string | Wpisane hasło; zapis z kodu też działa. |
PasswordChar | char | Znak maskujący, domyślnie ●. |
MaxLength | int | Maksymalna długość hasła. |
PasswordChanged | zdarzenie | Po każdej zmianie; parametr RoutedEventArgs. |
Clear() | metoda | Czyści pole. |
char.IsUpper, IsLower, IsDigit, IsLetterOrDigit | bool | Rodzaj znaku. |
warunek ? a : b | operator | Zwraca a, gdy warunek prawdziwy, w przeciwnym razie b. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
Haslo.Text | CS1061 — PasswordBox nie ma Text. Użyj Password. |
PasswordChanged z parametrem TextChangedEventArgs | Błąd kompilacji — parametr to RoutedEventArgs. |
TextChanged="…" na PasswordBox | Błą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
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.
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.
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
Contentprzycisku opcji;PasswordCharma typchar, więc weź pierwszy znak napisu:$"{opcja.Content}"[0].
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ć.
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) / 1e10daje sekundy. Potem dziel przez 60, 3600, 86400, 31 536 000 i wybierz jednostkę wif. - Format z odstępami tysięcy:
{wartosc:N0}.
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.
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 przezVisibility. - Zamaskowane hasło:
new string('●', haslo.Length).
Co trzeba zapamiętać
PasswordBox— hasło wPassword(nieText), maskowane znakiemPasswordChar, bez kopiowania.PasswordChangedreaguje na każdą zmianę; parametr toRoutedEventArgs.char.IsUpper,IsLower,IsDigit,IsLetterOrDigitw pętli po znakach sprawdzają skład hasła.- „Pokaż hasło” =
PasswordBoxiTextBoxw 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.