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

TextBox i zamiana tekstu na liczby

Prawie każde zadanie egzaminacyjne z aplikacji desktopowej zaczyna się od odczytania pola, w które użytkownik coś wpisał. TextBox to takie pole. Nauczysz się odczytywać i zmieniać jego tekst, bezpiecznie zamieniać go na liczbę, reagować na błędne dane bez zamykania programu i budować pola wielowierszowe.

C# XAML TextBox TryParse Focus TextChanged 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Odczytasz i zmienisz zawartość pola właściwością Text.
  • Wyjaśnisz, dlaczego "2" + "3" daje "23".
  • Zamienisz tekst na liczbę metodą TryParse i obsłużysz błędne dane bez zamykania programu.
  • Wyjaśnisz, od czego zależy, czy separatorem dziesiętnym jest przecinek, czy kropka.
  • Przeniesiesz kursor do błędnego pola metodami Focus() i SelectAll().
  • Zbudujesz pole wielowierszowe, pole tylko do odczytu i licznik znaków na żywo.
TEORIA

Tekst to nie liczba

W konsoli Console.ReadLine() zwracało tekst i trzeba go było zamienić na liczbę. W oknie jest dokładnie tak samo: pole oddaje zawsze tekst — nawet gdy użytkownik wpisał same cyfry.

pułapka
// w polu A wpisano 2, w polu B wpisano 3
Wynik.Text = A.Text + B.Text;     // "23" — to sklejenie napisów, nie dodawanie!

Różnica polega na tym, że w oknie użytkownik może kliknąć „Oblicz” w dowolnym momencie — także przy pustym polu albo z literami w polu na liczbę. Program musi to przetrwać.

TEORIA

Odczyt i zapis tekstu

MainWindow.xaml
<StackPanel Margin="15">
    <TextBlock Text="Jak masz na imię?"/>
    <TextBox x:Name="Imie" Margin="0,5,0,10"/>
    <Button Content="Przywitaj" IsDefault="True" Click="Przywitaj_Click"/>
    <TextBlock x:Name="Powitanie" Margin="0,10,0,0" FontSize="16"/>
</StackPanel>
MainWindow.xaml.cs
private void Przywitaj_Click(object sender, RoutedEventArgs e)
{
    string imie = Imie.Text.Trim();          // odczyt + usunięcie spacji z brzegów

    if (string.IsNullOrWhiteSpace(imie))
    {
        Powitanie.Text = "Najpierw wpisz imię.";
        return;
    }

    Powitanie.Text = $"Cześć, {imie}!";
    Imie.Clear();                            // wyczyszczenie pola
}
TextBoxTextBlock
Do czegoużytkownik wpisuje daneprogram wyświetla tekst
Ramka i kursortaknie
TreśćTextText

Trim() usuwa przypadkowe spacje z początku i końca. string.IsNullOrWhiteSpace zwraca true także dla tekstu złożonego z samych spacji — samo porównanie == "" by go przepuściło.

TEORIA

Parse czy TryParse?

MetodaGdy tekst to „12”Gdy tekst to „abc” albo pusty
int.Parse(tekst)zwraca 12wyjątek FormatException — program się zamyka
int.TryParse(tekst, out int x)zwraca true, x = 12zwraca false, x = 0 — program działa dalej
MainWindow.xaml.cs
private void Dodaj_Click(object sender, RoutedEventArgs e)
{
    if (int.TryParse(A.Text, out int a) && int.TryParse(B.Text, out int b))
    {
        Wynik.Text = $"Suma: {a + b}";
    }
    else
    {
        Wynik.Text = "Wpisz dwie liczby całkowite.";
    }
}

//                       ┌── tekst do zamiany
//                       │        ┌── tu trafi liczba, jeśli się udało
//                       ↓        ↓
// bool ok = int.TryParse(A.Text, out int a);

TryParse oddaje dwie informacje: wynik metody (bool) mówi, czy się udało, a liczbę zapisuje w zmiennej po out. Zapis out int a od razu tworzy taką zmienną. W oknie, gdzie użytkownik może wpisać cokolwiek, do odczytu pól używamy zawsze TryParse.

TEORIA

Liczby z przecinkiem

Dla ułamków używasz double.TryParse (przy pieniądzach — decimal.TryParse). Separator dziesiętny zależy od ustawień regionalnych Windows. Przy polskich ustawieniach:

Wpisany tekstWynik double.TryParse
6,5true, liczba 6,5
6.5false — kropka nie jest polskim separatorem dziesiętnym
spacje przed i po 6,5true — spacje na brzegach są dozwolone
pustyfalse

Wynik w drugą stronę, z liczby na tekst, formatujesz w interpolacji: {x:F2} — dwa miejsca po przecinku, {x:F1} — jedno.

TEORIA

Reakcja na błędne dane

Gdy pole zawiera błąd, dobry program robi trzy rzeczy: pokazuje komunikat, przenosi kursor do złego pola i zaznacza jego treść, żeby wystarczyło zacząć pisać. Potem kończy metodę, nie licząc dalej:

MainWindow.xaml.cs
if (!int.TryParse(Wiek.Text, out int wiek) || wiek < 1 || wiek > 120)
{
    Komunikat.Text = "Wiek musi być liczbą od 1 do 120.";
    Komunikat.Foreground = Brushes.Red;
    Wiek.Focus();          // kursor wraca do pola
    Wiek.SelectAll();      // cały tekst zaznaczony
    return;                // dalej nie liczymy
}

Dzięki || zakres sprawdza się tylko wtedy, gdy zamiana się udała. A Button IsDefault="True" z lekcji 04 pozwala zatwierdzić formularz klawiszem Enter prosto z pola.

TEORIA

Pole wielowierszowe, tylko do odczytu i TextChanged

MainWindow.xaml
<StackPanel Margin="10">
    <TextBox x:Name="Notatka" Height="150"
             AcceptsReturn="True"
             TextWrapping="Wrap"
             VerticalScrollBarVisibility="Auto"
             MaxLength="200"
             TextChanged="Notatka_TextChanged"/>
    <TextBlock x:Name="Licznik" Text="0 / 200" Margin="0,5,0,0"/>
</StackPanel>
MainWindow.xaml.cs
// wywołuje się po KAŻDEJ zmianie tekstu: wpisaniu, usunięciu, wklejeniu, zmianie z kodu
private void Notatka_TextChanged(object sender, TextChangedEventArgs e)
{
    Licznik.Text = $"{Notatka.Text.Length} / {Notatka.MaxLength}";
}
WłaściwośćEfekt
AcceptsReturn="True"Enter tworzy nową linię zamiast zatwierdzać formularz.
TextWrapping="Wrap"Długi tekst zawija się do następnej linii.
VerticalScrollBarVisibility="Auto"Pasek przewijania pojawia się, gdy tekst się nie mieści.
MaxLength="200"Z klawiatury nie da się wpisać więcej niż 200 znaków. 0 = bez limitu.
IsReadOnly="True"Tekst można zaznaczyć i skopiować, ale nie zmienić — dobre na wyniki.

Pole w StackPanel potrzebuje wysokości

Pionowy StackPanel daje polu tyle wysokości, ile potrzebuje tekst — pusta notatka miałaby jedną linię. Dlatego pole wielowierszowe dostaje Height. Pole, które samo rośnie razem z oknem, zrobisz w lekcji 09.

PRZYKŁAD

Kalkulator spalania

Użytkownik wpisuje przejechany dystans, ilość paliwa i cenę litra. Program liczy spalanie na 100 km, koszt przejazdu i koszt kilometra. Każde pole jest sprawdzane osobno, a przy błędzie kursor wraca do złego pola z zaznaczonym tekstem.

Kalkulator spalania–□✕
Przejechany dystans (km):
420
Zużyte paliwo (l):
27,3
Cena za litr (zł):
6,19
Oblicz
Spalanie: 6,5 l/100 km
Koszt przejazdu: 168,99 zł
Koszt 1 km: 0,40 zł

wynik dla poprawnych danych

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja07.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Kalkulator spalania" Height="360" Width="420"
        WindowStartupLocation="CenterScreen">

    <StackPanel Margin="15">
        <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
            <TextBlock Text="Przejechany dystans (km):" Width="180" VerticalAlignment="Center"/>
            <TextBox x:Name="Dystans" Width="150" MaxLength="7"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
            <TextBlock Text="Zużyte paliwo (l):" Width="180" VerticalAlignment="Center"/>
            <TextBox x:Name="Paliwo" Width="150" MaxLength="6"/>
        </StackPanel>

        <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
            <TextBlock Text="Cena za litr (zł):" Width="180" VerticalAlignment="Center"/>
            <TextBox x:Name="Cena" Width="150" MaxLength="5"/>
        </StackPanel>

        <Button Content="Oblicz" Padding="8,4" Margin="0,5,0,10"
                IsDefault="True" Click="Oblicz_Click"/>

        <TextBox x:Name="Wynik" Height="80"
                 IsReadOnly="True" TextWrapping="Wrap" FontSize="14"
                 Background="#FFF5F5F5"/>
    </StackPanel>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;

namespace Lekcja07;

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

    private void Oblicz_Click(object sender, RoutedEventArgs e)
    {
        // 1. Zamiana tekstu na liczby — każde pole sprawdzamy osobno
        if (!double.TryParse(Dystans.Text, out double dystans) || dystans <= 0)
        {
            PokazBlad("Dystans musi być liczbą większą od zera.", Dystans);
            return;
        }

        if (!double.TryParse(Paliwo.Text, out double paliwo) || paliwo <= 0)
        {
            PokazBlad("Ilość paliwa musi być liczbą większą od zera.", Paliwo);
            return;
        }

        if (!double.TryParse(Cena.Text, out double cena) || cena <= 0)
        {
            PokazBlad("Cena musi być liczbą większą od zera.", Cena);
            return;
        }

        // 2. Obliczenia
        double spalanie = paliwo / dystans * 100;
        double koszt = paliwo * cena;
        double kosztKm = koszt / dystans;

        // 3. Wynik — liczby zamieniamy z powrotem na tekst
        Wynik.Text = $"Spalanie: {spalanie:F1} l/100 km\n" +
                     $"Koszt przejazdu: {koszt:F2} zł\n" +
                     $"Koszt 1 km: {kosztKm:F2} zł";
    }

    // Wspólna obsługa błędu: komunikat + kursor w złym polu
    private void PokazBlad(string komunikat, TextBox pole)
    {
        Wynik.Text = komunikat;
        pole.Focus();
        pole.SelectAll();
    }
}

Co dzieje się po kolei

  • Każdy wiersz formularza to poziomy panel: napis o stałej szerokości 180 i pole o szerokości 150 — pola tworzą równą kolumnę.
  • MaxLength ogranicza długość wpisu, np. dystans do 7 znaków — chroni przed przypadkowym wklejeniem długiego tekstu.
  • IsDefault="True": po wpisaniu ceny wystarczy nacisnąć Enter.
  • Wynik jest w polu TextBox z IsReadOnly="True" — użytkownik nie zmieni wyniku, ale może go skopiować. Tło #FFF5F5F5 odróżnia je od pól do wpisywania.
  • Każde pole sprawdza osobny if: „to nie liczba” albo „liczba nie jest dodatnia”. Zmienna z out jest dostępna niżej, w obliczeniach.
  • Przy błędzie PokazBlad wpisuje komunikat, przenosi kursor i zaznacza tekst złego pola, a return kończy metodę — nie liczymy na złych danych (np. dzieląc przez 0).
  • PokazBlad ma parametr typu TextBox, więc ta sama metoda obsługuje każde z trzech pól.
  • Spalanie: 27,3 l / 420 km × 100 = 6,5 l. Wszystkie zmienne to double, więc nie ma dzielenia całkowitego.
  • "\n" w wyniku przenosi tekst do nowej linii, a {koszt:F2} zaokrągla kwotę do groszy: 168,987 → 168,99.

Sprawdź trzy rzeczy

  1. Wpisz w pole paliwa 27.3 (z kropką). Na polskim Windowsie pojawi się komunikat o błędzie, a tekst w polu zostanie zaznaczony.
  2. Zamień pierwszy double.TryParse na double.Parse i kliknij Oblicz przy pustym polu. Program zakończy się wyjątkiem FormatException.
  3. Usuń return; z bloku sprawdzającego dystans, wpisz dystans 0 i kliknij Oblicz. Komunikat o błędzie zostanie od razu zastąpiony wynikiem ze znakiem „∞” — dzielenie przez zero na typie double nie zamyka programu, tylko daje bezsensowny wynik.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTyp / zwracaCo robi
TextBox.TextstringTreść pola. Puste pole to "", nigdy null.
MaxLengthintLimit znaków wpisywanych z klawiatury.
IsReadOnlyboolBlokuje edycję, pozwala kopiować.
AcceptsReturn, TextWrapping, VerticalScrollBarVisibility—Razem: pole wielowierszowe z przewijaniem.
Focus()boolPrzenosi kursor do pola.
SelectAll()nicZaznacza cały tekst.
Clear()nicCzyści pole.
TextChangedzdarzeniePo każdej zmianie tekstu. Parametr TextChangedEventArgs.
int.TryParse(s, out int x)boolTekst → liczba całkowita bez ryzyka wyjątku.
double.TryParse, decimal.TryParseboolUłamki; separator według ustawień Windows.
Trim()stringUsuwa białe znaki z brzegów.
string.IsNullOrWhiteSpace(s)boolPusty albo same spacje.
{x:F2}formatowanieDwa miejsca po przecinku.
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
int wiek = int.Parse(Wiek.Text);Przy pustym polu FormatException i zamknięcie programu. Użyj TryParse.
Wynik.Text = suma;CS0029 — liczba to nie tekst. Użyj $"{suma}" albo suma.ToString().
if (Wiek.Text > 18)CS0019 — nie da się porównać tekstu z liczbą. Najpierw TryParse.
A.Text + B.TextSklejenie napisów: „2” + „3” = „23”.
<TextBlock x:Name="Imie"/> jako pole do wpisywaniaTextBlock tylko wyświetla. Do wpisywania potrzebny jest TextBox.
if (Imie.Text == null)Nigdy nie będzie prawdą — puste pole to "".
brak return po komunikacie o błędzieProgram liczy dalej na złych danych.
Focus() w konstruktorze oknaOkno jeszcze nie jest widoczne, fokus nie zostanie ustawiony.
pole wielowierszowe w StackPanel bez HeightMa wysokość jednej linii.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Komunikaty o błędach wyświetlaj w napisie w oknie. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Przelicznik walut★☆☆

Kantor szkolnej wymiany: pole „Kwota w zł” i przyciski na EUR, na USD, na GBP. Kursy zapisz jako stałe (np. 4,30; 3,95; 5,10). Wynik: „250,00 zł = 58,14 EUR”. Przy pustym polu, tekście albo kwocie ujemnej pokaż czerwony komunikat i przenieś kursor do pola. Trzy przyciski obsługuje jedna metoda — kurs i symbol waluty są w Tag.

Kantor–□✕
Kwota w zł:
250
na EUR
na USD
na GBP
250,00 zł = 58,14 EUR
pokaż wskazówkę
  • W Tag możesz zapisać np. "4,30;EUR" i rozdzielić przez Split(';'). Kurs zamień przez decimal.Parse — tę wartość wpisałeś sam.
  • Kwotę użytkownika zamieniaj przez decimal.TryParse.
ZAD 2Ocena z punktów★☆☆

Nauczyciel wpisuje liczbę zdobytych punktów (całkowitą) i maksymalną liczbę punktów ze sprawdzianu. Program pokazuje procent z jednym miejscem po przecinku i ocenę według progów: 0–39 % niedostateczny, 40–54 % dopuszczający, 55–69 % dostateczny, 70–84 % dobry, 85–94 % bardzo dobry, 95–100 % celujący. Odrzuć: puste pola, wartości ujemne, maksimum równe 0 i punkty większe od maksimum. W komentarzu wypisz sześć zestawów danych, którymi sprawdziłeś granice progów.

pokaż wskazówkę
  • Procent licz na double: punkty * 100.0 / maksimum — inaczej dzielenie całkowite zgubi część ułamkową.
  • Sprawdzaj pola po kolei i po pierwszym błędzie kończ metodę.
ZAD 3Wpis z limitem znaków★☆☆

Okno do pisania krótkiego ogłoszenia na tablicę szkolną: wielowierszowe pole (maks. 160 znaków) i pod nim licznik „37 / 160”, aktualizowany przy każdym naciśnięciu klawisza. Gdy zostanie mniej niż 20 znaków, licznik robi się czerwony, przy mniej niż 50 — pomarańczowy. Przycisk Opublikuj przepisuje treść do napisu „Na tablicy:” pod spodem i czyści pole; przy pustym polu nic nie publikuje.

pokaż wskazówkę
  • Licznik i kolor ustawiaj w TextChanged. Pamiętaj o gałęzi przywracającej kolor czarny.
  • Nie ustawiaj atrybutu Text polu w XAML — TextChanged mógłby się wywołać, zanim powstanie licznik.
ZAD 4Średnia ocen z jednego pola★★☆

Uczeń wpisuje oceny w jednym polu, oddzielone spacjami, np. 5 4 3 6 4. Po kliknięciu Policz program pokazuje liczbę ocen, średnią z dwoma miejscami po przecinku, najwyższą i najniższą ocenę oraz informację „Średnia uprawnia do paska!”, gdy średnia wynosi co najmniej 4,75. Jeśli któryś element nie jest oceną od 1 do 6, program wypisuje dokładnie, który to element: „„7” nie jest oceną od 1 do 6”.

pokaż wskazówkę
  • Split(' ', StringSplitOptions.RemoveEmptyEntries) pominie puste kawałki przy podwójnych spacjach.
  • Najwyższą i najniższą znajdziesz w tej samej pętli, w której liczysz sumę.
ZAD 5Notatnik z blokadą★★☆

Wielowierszowe pole (wysokość 200, z paskiem przewijania) i pod nim rząd przycisków: WIELKIE LITERY, małe litery, Wyczyść i Zablokuj. Ostatni przełącza pole w tryb tylko do odczytu i zmienia swój napis na Odblokuj; w trybie blokady pozostałe przyciski są nieaktywne. Pod przyciskami: „Znaków: 120, wierszy: 4”.

pokaż wskazówkę
  • ToUpper() i ToLower() zwracają nowy napis — trzeba go przypisać z powrotem do Text.
  • Trzy przyciski do blokowania włóż do jednego poziomego panelu z nazwą i ustawiaj jego IsEnabled.
  • Wiersze policzysz przez Split('\n').Length.
ZAD 6Rejestracja na turniej★★★

Formularz zapisu na szkolny turniej: pola Nick, Wiek, E-mail i przycisk Zarejestruj. Zasady:

  • nick: 3–16 znaków, bez spacji;
  • wiek: liczba całkowita od 13 do 19 (turniej dla uczniów szkół średnich);
  • e-mail: dokładnie jeden znak @, który nie jest pierwszym ani ostatnim znakiem, i co najmniej jedna kropka po @.

Przy pierwszym napotkanym błędzie pokaż czerwony komunikat, przenieś kursor do złego pola i zaznacz jego treść. Gdy wszystko jest dobrze, dodaj gracza do listy pod formularzem („3. Gracz123 (16 lat)”), wyczyść pola i ustaw kursor w polu nicku. Po zapisaniu 8 graczy wyświetl „Lista pełna” i zablokuj formularz.

pokaż wskazówkę
  • Wspólna metoda błędu jak PokazBlad z przykładu oszczędzi powtarzania.
  • IndexOf('@') zwraca pozycję pierwszego @ albo −1; LastIndexOf('@') — ostatniego. Kropkę po @ znajdziesz przez IndexOf('.', pozycja).
  • Listę zbuduj z napisów dodawanych do pustego StackPanel (lekcja 03), a pola formularza włóż do osobnego panelu, który zablokujesz.
PODSUMOWANIE

Co trzeba zapamiętać

  • TextBox.Text to zawsze string. Puste pole to "".
  • Do liczb używaj TryParse i obsłuż wynik false; Parse na złych danych zamyka program.
  • Separator dziesiętny zależy od ustawień Windows — w Polsce to przecinek.
  • Przy błędzie: komunikat, Focus(), SelectAll() i return.
  • AcceptsReturn + TextWrapping + VerticalScrollBarVisibility (+ Height w StackPanel) = pole wielowierszowe.
  • IsReadOnly — tylko do odczytu, z możliwością kopiowania; TextChanged — reakcja na każdą zmianę tekstu.

Dokumentacja: Microsoft Learn — klasa TextBox, int.TryParse.

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