TextBox – pole tekstowe
Poznasz TextBox – kontrolkę do wpisywania tekstu przez użytkownika. Nauczysz się ograniczać długość wpisywanego tekstu, tworzyć pola wieloliniowe, reagować na zmiany w czasie pisania i walidować dane.
Czym jest TextBox?
TextBox to pole tekstowe, w które użytkownik może wpisywać tekst – imię, adres email, opis, liczbę, dowolną treść. To podstawowa kontrolka każdego formularza.
TextBox = pole do wpisywania tekstu przez użytkownika.
TextBlock = wyświetla tekst, nie można go edytować.
Jeśli chcesz pokazać wynik obliczeń, opis, etykietę → TextBlock.
Jeśli chcesz, żeby użytkownik coś wpisał → TextBox.
<TextBox x:Name="txtImie" Width="200" Height="30" />
Text – podstawowa właściwość
Właściwość Text przechowuje to co użytkownik wpisał.
Możesz ją odczytać, zmienić lub ustawić wartość domyślną.
<!-- Tekst domyślny widoczny od razu po otwarciu okna --> <TextBox x:Name="txtMiasto" Text="Lębork" Width="200" />
// Odczyt tego co wpisał użytkownik string imie = txtImie.Text; // Ustawienie nowej wartości (np. po wczytaniu danych) txtImie.Text = "Anna"; // Połączenie z innym tekstem txtImie.Text = $"Witaj, {imie}!";
Nawet jeśli użytkownik wpisze same cyfry, Text zwraca
je jako tekst (string), nie jako liczbę.
Jeśli potrzebujesz liczby, musisz ją skonwertować –
patrz sekcja o walidacji wejścia.
MaxLength – ograniczenie długości
MaxLength ogranicza ile znaków użytkownik może wpisać.
Po osiągnięciu limitu pole po prostu nie przyjmuje więcej znaków –
nie trzeba samodzielnie tego sprawdzać.
<!-- Kod pocztowy – maksymalnie 6 znaków (00-000) --> <TextBox x:Name="txtKodPocztowy" MaxLength="6" Width="80" /> <!-- Nazwa użytkownika – maksymalnie 20 znaków --> <TextBox x:Name="txtNazwa" MaxLength="20" Width="200" /> <!-- Numer PESEL – zawsze 11 cyfr --> <TextBox x:Name="txtPesel" MaxLength="11" Width="120" />
MaxLength="11" nie sprawdza czy użytkownik wpisał same cyfry –
tylko że nie wpisał więcej niż 11 znaków (mogą to być dowolne litery).
Do sprawdzenia czy wpisane znaki są poprawne (np. tylko cyfry)
potrzebujesz dodatkowej walidacji – patrz sekcja 7.
<!-- XAML: --> <TextBox x:Name="txtOpis" MaxLength="200" TextChanged="txtOpis_TextChanged" /> <TextBlock x:Name="lblLicznik" Text="0/200" /> // C#: private void txtOpis_TextChanged(object sender, TextChangedEventArgs e) { lblLicznik.Text = $"{txtOpis.Text.Length}/200"; }
IsReadOnly – pole tylko do odczytu
IsReadOnly="True" blokuje możliwość edycji tekstu,
ale w przeciwieństwie do IsEnabled="False":
| Cecha | IsReadOnly=True | IsEnabled=False |
|---|---|---|
| Można edytować tekst | ❌ Nie | ❌ Nie |
| Wygląd | ✅ Normalny (czarny tekst) | ⚠️ Wyszarzony |
| Można zaznaczyć i skopiować tekst | ✅ Tak | ❌ Nie |
| Pole dostępne w nawigacji Tab | ✅ Tak | ❌ Nie |
<!-- Pole z automatycznie wygenerowanym numerem zamówienia – użytkownik widzi i może skopiować, ale nie edytować --> <TextBox x:Name="txtNumerZamowienia" Text="ZAM/2024/00142" IsReadOnly="True" Background="#f0f0f0" Width="150" />
Użyj IsReadOnly, gdy chcesz, żeby użytkownik mógł przeczytać i skopiować wartość (np. wygenerowany kod, ID, wynik). Użyj IsEnabled=”False”, gdy pole jest tymczasowo niedostępne i nie powinno przyciągać uwagi (np. zależne od innego wyboru).
Pole wieloliniowe
Domyślnie TextBox to pole jednoliniowe – naciśnięcie Enter nic nie robi
(lub aktywuje przycisk z IsDefault="True", jeśli taki jest w oknie).
Żeby zrobić pole wieloliniowe (jak w notatniku), potrzebujesz dwóch właściwości.
AcceptsReturn – zezwolenie na Enter
AcceptsReturn="True" sprawia, że naciśnięcie Enter
wstawia nową linię w polu, zamiast aktywować przycisk domyślny.
<TextBox x:Name="txtTresc" AcceptsReturn="True" Height="150" Width="300" />
TextWrapping – zawijanie długich linii
Sam AcceptsReturn pozwala na Enter, ale długie linie
nadal wychodzą poza pole w poziomie. TextWrapping="Wrap"
sprawia, że linie zawijają się automatycznie.
<TextBox x:Name="txtOpis" AcceptsReturn="True" TextWrapping="Wrap" Height="150" Width="300" /> <!-- Teraz: Enter tworzy nową linię, a długie linie zawijają się automatycznie -->
Scrollbary – pasek przewijania
Gdy treść jest dłuższa niż wysokość pola, potrzebny jest scrollbar.
Włączasz go przez VerticalScrollBarVisibility.
| Wartość | Zachowanie |
|---|---|
Disabled (domyślne) | Brak scrollbara – treść poza polem jest niewidoczna |
Auto | Scrollbar pojawia się tylko gdy potrzebny |
Visible | Scrollbar zawsze widoczny |
<TextBox x:Name="txtNotatnik" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" FontFamily="Consolas" FontSize="14" Padding="8" AcceptsTab="True" /> <!-- AcceptsTab – Tab wpisuje znak tabulacji zamiast przejść do następnej kontrolki -->
Zdarzenie TextChanged
TextChanged odpala się za każdym razem gdy zmieni się
zawartość pola – po każdym wpisanym lub usuniętym znaku.
Dzięki temu możesz reagować na pisanie w czasie rzeczywistym, bez przycisku.
<TextBox x:Name="txtImie" TextChanged="txtImie_TextChanged" />
private void txtImie_TextChanged(object sender, TextChangedEventArgs e) { // 1. Aktualizacja podglądu na żywo lblPodglad.Text = $"Witaj, {txtImie.Text}!"; // 2. Włączanie/wyłączanie przycisku w zależności od treści btnDalej.IsEnabled = !string.IsNullOrWhiteSpace(txtImie.Text); // 3. Licznik znaków lblLicznik.Text = $"{txtImie.Text.Length} znaków"; }
Jeśli w Window_Loaded ustawisz txtImie.Text = "Jan";,
zdarzenie TextChanged odpali się natychmiast.
Jeśli metoda obsługująca to zdarzenie odwołuje się do innych kontrolek,
które mogą jeszcze nie być gotowe, dodaj zabezpieczenie:
if (lblPodglad == null) return; na początku metody.
Walidacja wejścia
Walidacja to sprawdzanie, czy wpisane dane są poprawne. Są trzy podejścia – każde ma swoje zastosowanie.
Walidacja przy kliknięciu przycisku
Najprostsze podejście – sprawdzamy wszystko na raz, gdy użytkownik kliknie przycisk „Zapisz” lub „Dalej”.
private void btnZapisz_Click(object sender, RoutedEventArgs e) { // Sprawdzenie 1: pole nie może być puste if (string.IsNullOrWhiteSpace(txtImie.Text)) { MessageBox.Show("Podaj imię!", "Błąd", MessageBoxButton.OK, MessageBoxImage.Warning); txtImie.Focus(); // ustawiamy kursor na polu z błędem return; } // Sprawdzenie 2: czy wiek jest liczbą if (!int.TryParse(txtWiek.Text, out int wiek)) { MessageBox.Show("Wiek musi być liczbą!", "Błąd", MessageBoxButton.OK, MessageBoxImage.Warning); txtWiek.Focus(); return; } // Sprawdzenie 3: zakres wartości if (wiek < 0 || wiek > 120) { MessageBox.Show("Wiek musi być w zakresie 0-120!", "Błąd", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // Wszystko OK – kontynuujemy MessageBox.Show($"Zapisano: {txtImie.Text}, {wiek} lat"); }
Walidacja na żywo (podczas pisania)
Drugie podejście – pokazujemy błąd natychmiast, gdy użytkownik pisze, zamiast czekać na kliknięcie przycisku.
private void txtEmail_TextChanged(object sender, TextChangedEventArgs e) { string email = txtEmail.Text; bool czyPoprawny = email.Contains("@") && email.Contains(".") && email.Length > 5; if (czyPoprawny) { txtEmail.BorderBrush = Brushes.LimeGreen; lblBladEmail.Visibility = Visibility.Collapsed; } else { txtEmail.BorderBrush = Brushes.Tomato; lblBladEmail.Text = "Nieprawidłowy adres email"; lblBladEmail.Visibility = Visibility.Visible; } }
Blokowanie złych znaków podczas pisania
Trzecie podejście – nie pozwalamy wpisać złych znaków
wcale. Używamy do tego zdarzenia PreviewTextInput,
które odpala się przed dodaniem znaku do pola.
<!-- XAML: --> <TextBox x:Name="txtWiek" PreviewTextInput="txtWiek_PreviewTextInput" /> // C#: private void txtWiek_PreviewTextInput(object sender, TextCompositionEventArgs e) { // e.Text to znak, który użytkownik właśnie próbuje wpisać foreach (char znak in e.Text) { if (!char.IsDigit(znak)) { e.Handled = true; // blokujemy wpisanie znaku return; } } }
Ustawienie e.Handled = true mówi WPF: „zignoruj to zdarzenie,
nie wpisuj znaku do pola”. To ta sama mechanika, którą poznałeś
przy zdarzeniach myszy w Canvas.
| Podejście | Kiedy używać |
|---|---|
| Walidacja po kliknięciu | Formularze, gdzie sprawdzasz wszystko na raz przed zapisem |
| Walidacja na żywo | Pokazywanie błędu od razu, np. format email, siła hasła |
| Blokowanie znaków | Pola tylko-cyfry, tylko-litery (PESEL, telefon, kod) |
Czyszczenie pola
// Sposób 1: ustawienie pustego tekstu txtImie.Text = ""; // Sposób 2: metoda Clear() – działa identycznie txtImie.Clear(); // Czyszczenie kilku pól na raz private void WyczyscFormularz() { txtImie.Clear(); txtNazwisko.Clear(); txtEmail.Clear(); txtWiek.Clear(); txtImie.Focus(); // ustawiamy kursor na pierwszym polu } // Przycisk "Wyczyść" private void btnWyczysc_Click(object sender, RoutedEventArgs e) { WyczyscFormularz(); }
private void btnDodaj_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrWhiteSpace(txtNazwa.Text)) { MessageBox.Show("Podaj nazwę produktu!"); return; } // Dodajemy do listy lstProdukty.Items.Add(txtNazwa.Text); // Czyścimy pole i ustawiamy focus, żeby można dodawać kolejne txtNazwa.Clear(); txtNazwa.Focus(); }
Inne przydatne właściwości
| Właściwość | Co robi |
|---|---|
TextAlignment | Wyrównanie tekstu w polu (Left/Center/Right) |
CharacterCasing | Wymusza wielkość liter: Upper, Lower, Normal |
SelectionStart / SelectionLength | Zaznaczenie fragmentu tekstu z kodu |
CaretIndex | Pozycja kursora w tekście |
SpellCheck.IsEnabled | Sprawdzanie pisowni (czerwone podkreślenie błędów) |
UndoLimit | Ile kroków Ctrl+Z można wykonać |
<!-- Kod promocyjny – zawsze wielkimi literami --> <TextBox x:Name="txtKodPromocyjny" CharacterCasing="Upper" MaxLength="10" /> <!-- Użytkownik wpisuje "lato2025", pole pokazuje "LATO2025" -->
// Zaznacz cały tekst w polu (przydaje się np. po Focus()) txtImie.SelectAll(); // Ustaw kursor na końcu tekstu txtImie.CaretIndex = txtImie.Text.Length; // Zaznacz konkretny fragment (od znaku 0, długość 5) txtImie.Select(0, 5);
Praktyczny przykład – formularz rejestracji
<Grid Margin="20"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="110" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="Nazwa użytkownika:" VerticalAlignment="Center" Margin="0,0,0,8" /> <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtNazwa" MaxLength="20" Margin="0,0,0,8" TextChanged="txtNazwa_TextChanged" /> <TextBlock Grid.Row="1" Grid.Column="0" Text="Email:" VerticalAlignment="Center" Margin="0,0,0,8" /> <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtEmail" Margin="0,0,0,8" /> <TextBlock Grid.Row="2" Grid.Column="0" Text="Wiek:" VerticalAlignment="Center" Margin="0,0,0,8" /> <TextBox Grid.Row="2" Grid.Column="1" x:Name="txtWiek" MaxLength="3" PreviewTextInput="txtWiek_PreviewTextInput" Margin="0,0,0,8" Width="60" HorizontalAlignment="Left" /> <TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" x:Name="lblBlad" Foreground="Tomato" Margin="0,0,0,8" Visibility="Collapsed" /> <StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="Wyczyść" Width="80" Margin="0,0,8,0" Click="btnWyczysc_Click" /> <Button x:Name="btnZarejestruj" Content="Zarejestruj" Width="100" IsEnabled="False" Click="btnZarejestruj_Click" /> </StackPanel> </Grid>
private void txtNazwa_TextChanged(object sender, TextChangedEventArgs e) { // Przycisk aktywny tylko gdy nazwa ma min. 3 znaki btnZarejestruj.IsEnabled = txtNazwa.Text.Length >= 3; } private void txtWiek_PreviewTextInput(object sender, TextCompositionEventArgs e) { // Tylko cyfry w polu wieku foreach (char znak in e.Text) { if (!char.IsDigit(znak)) { e.Handled = true; return; } } } private void btnZarejestruj_Click(object sender, RoutedEventArgs e) { // Walidacja emaila if (!txtEmail.Text.Contains("@") || !txtEmail.Text.Contains(".")) { PokazBlad("Podaj prawidłowy adres email"); txtEmail.Focus(); return; } // Walidacja wieku if (!int.TryParse(txtWiek.Text, out int wiek) || wiek < 13) { PokazBlad("Wiek musi być liczbą i wynosić minimum 13 lat"); txtWiek.Focus(); return; } // Sukces lblBlad.Visibility = Visibility.Collapsed; MessageBox.Show($"Zarejestrowano: {txtNazwa.Text} ({wiek} lat)", "Sukces", MessageBoxButton.OK, MessageBoxImage.Information); WyczyscFormularz(); } private void PokazBlad(string komunikat) { lblBlad.Text = komunikat; lblBlad.Visibility = Visibility.Visible; } private void WyczyscFormularz() { txtNazwa.Clear(); txtEmail.Clear(); txtWiek.Clear(); txtNazwa.Focus(); } private void btnWyczysc_Click(object sender, RoutedEventArgs e) { WyczyscFormularz(); lblBlad.Visibility = Visibility.Collapsed; }
Częste błędy
❌ Błąd 1: Konwersja Text na liczbę bez TryParse
❌ Wyjątek przy nieprawidłowych danych
int wiek = int.Parse(txtWiek.Text); // Jeśli użytkownik wpisze "abc" – // program się wyłoży (wyjątek)!
✅ TryParse – bezpieczna konwersja
if (!int.TryParse(txtWiek.Text, out int wiek))
{
MessageBox.Show("Wiek musi być liczbą!");
return;
}
// Tutaj wiek jest już bezpieczną liczbą
❌ Błąd 2: Pole wieloliniowe bez AcceptsReturn
❌ Enter nie działa
<TextBox Height="150" />
<!-- Enter nic nie robi,
tekst nie zawija się -->
✅ AcceptsReturn + TextWrapping
<TextBox Height="150"
AcceptsReturn="True"
TextWrapping="Wrap" />
❌ Błąd 3: Sprawdzanie tylko IsNullOrEmpty (nie wykrywa spacji)
❌ Same spacje przechodzą walidację
if (string.IsNullOrEmpty(txtImie.Text))
{
MessageBox.Show("Podaj imię!");
}
// Użytkownik wpisał same spacje " " –
// walidacja nie wykryje problemu!
✅ IsNullOrWhiteSpace
if (string.IsNullOrWhiteSpace(txtImie.Text))
{
MessageBox.Show("Podaj imię!");
}
// Wykrywa też same spacje
❌ Błąd 4: TextChanged odpalający się przy starcie programu
❌ Wyjątek null reference przy starcie
private void txt_TextChanged(...)
{
// lblInfo może być jeszcze null
// gdy ten kod wykona się podczas
// budowania okna!
lblInfo.Text = txt.Text;
}
✅ Zabezpieczenie przed null
private void txt_TextChanged(...)
{
if (lblInfo == null) return;
lblInfo.Text = txt.Text;
}
Zadania do wykonania
Stwórz pole TextBox z MaxLength="50" i TextBlock
pokazujący licznik znaków w formacie „15/50″,
aktualizowany na żywo (TextChanged).
Gdy zostanie wykorzystane więcej niż 80% limitu, licznik
ma zmienić kolor na pomarańczowy.
Stwórz pole na numer telefonu, które przyjmuje
tylko cyfry (użyj PreviewTextInput)
i ma MaxLength="9". Dodaj TextBlock, który
informuje czy numer jest kompletny (9 cyfr) czy nie.
Zbuduj prosty notatnik: wieloliniowe pole TextBox
(AcceptsReturn, TextWrapping, scrollbar), przycisk „Wyczyść”
oraz pasek stanu pokazujący liczbę znaków i liczbę linii
(txt.Text.Split('\n').Length), aktualizowany
przy każdej zmianie tekstu.
Stwórz kompletny formularz rejestracyjny z polami: nazwa użytkownika (3-20 znaków), email (musi zawierać @ i .), wiek (tylko cyfry, 13-120), hasło (min. 6 znaków – użyj PasswordBox). Waliduj wszystko po kliknięciu „Zarejestruj”, wyświetlaj błędy w czerwonym TextBlock, a przycisk „Zarejestruj” ma być nieaktywny dopóki nazwa użytkownika ma mniej niż 3 znaki. Po sukcesie wyczyść formularz i pokaż MessageBox z podsumowaniem.