Slider — suwak
Głośność, jasność, rozmiar czcionki, temperatura — gdy liczba ma leżeć w znanym zakresie, wygodniej przesunąć suwak niż ją wpisywać. Suwak sam pilnuje zakresu, więc nie trzeba sprawdzać danych tak jak w polu tekstowym. Nauczysz się ustawiać zakres i krok, dodawać podziałkę i reagować na przesuwanie na żywo.
Czego się dziś nauczysz
- Dodasz suwak z zakresem
Minimum–Maximumi wartością początkową. - Ustawisz krok, o jaki zmienia się wartość, i pokażesz podziałkę.
- Zareagujesz na przesuwanie zdarzeniem
ValueChangedi zabezpieczysz je na starcie okna. - Wyświetlisz wartość suwaka w czytelnej postaci, z jednostką.
- Zmienisz wartość i zakres suwaka z kodu.
Zakres i wartość
<StackPanel Margin="20">
<TextBlock Text="Głośność dzwonka"/>
<Slider x:Name="Glosnosc" Minimum="0" Maximum="100" Value="40"/>
</StackPanel>
suwak na wartości 40 z zakresu 0–100
Value ma typ double i zawsze leży między Minimum a Maximum — próba ustawienia większej wartości kończy się ustawieniem maksimum. Suwak obsługuje też klawiatura: strzałki zmieniają wartość o SmallChange (domyślnie 0,1), a PgUp/PgDn i kliknięcie w pasek obok uchwytu — o LargeChange (domyślnie 1). Atrybut Orientation="Vertical" stawia suwak pionowo.
Krok i podziałka
Przeciągany myszą suwak przyjmuje dowolne wartości ułamkowe, np. 40,3719. Żeby wartość „przeskakiwała” co określony krok, łączysz dwie właściwości:
<!-- tylko liczby całkowite od 1 do 10, kreski przy każdej -->
<Slider x:Name="Ocena" Minimum="1" Maximum="10" Value="5"
TickFrequency="1" IsSnapToTickEnabled="True"
TickPlacement="BottomRight" AutoToolTipPlacement="TopLeft"/>
| Właściwość | Co robi |
|---|---|
TickFrequency | Odstęp między kreskami podziałki, np. 1, 0,5, 10. |
IsSnapToTickEnabled="True" | Wartość zawsze „przyskakuje” do najbliższej kreski — to ustawia krok. |
TickPlacement | Gdzie narysować kreski: None (domyślnie — niewidoczne), TopLeft, BottomRight, Both. Kreski działają także, gdy ich nie widać. |
AutoToolTipPlacement | Podczas przeciągania pokazuje dymek z wartością. |
Przy kroku 0,5 na zakresie 120 jednostek kresek byłoby 240 — wtedy lepiej ich nie rysować (TickPlacement zostaje None), a krok i tak działa.
Zdarzenie ValueChanged
Wywołuje się przy każdej zmianie wartości — podczas przeciągania nawet kilkadziesiąt razy na sekundę. Parametr ma dość długi typ RoutedPropertyChangedEventArgs<double>, który przechowuje starą i nową wartość:
// w XAML: <Slider x:Name="Glosnosc" ... ValueChanged="Glosnosc_ValueChanged"/>
private void Glosnosc_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (Opis == null)
{
return; // Value="40" w XAML wywołało zdarzenie przed utworzeniem napisu
}
Opis.Text = $"Głośność: {e.NewValue:F0}%"; // to samo co Glosnosc.Value
}
Zdarzenie przy starcie okna
Value="40" w XAML zmienia wartość z domyślnego 0 na 40 i wywołuje ValueChanged w trakcie InitializeComponent(). Jeśli metoda odwołuje się do kontrolek zapisanych niżej w pliku, sprawdź na początku, czy nie są null — tak jak przy polach wyboru i listach.
Suwak z kodu
Glosnosc.Value = 75; // przesuwa uchwyt (i wywołuje ValueChanged)
Glosnosc.Maximum = 50; // nowy zakres — Value spada do 50
int procent = (int)Glosnosc.Value; // double → int: ucina część ułamkową
double zaokr = Math.Round(Glosnosc.Value, 1); // zaokrąglenie do 0,1
Pamiętaj, że Value to double. Do wyświetlania używaj formatu ({x:F0}, {x:F1}), a do obliczeń na liczbach całkowitych — rzutowania (int) albo Math.Round.
Kalkulator BMI z suwakami
Dwa suwaki: wzrost (120–220 cm, co 1 cm) i waga (30–150 kg, co 0,5 kg). Wskaźnik BMI, kategoria i kolor karty wyniku zmieniają się podczas przesuwania. Pod wynikiem program podaje zakres wagi, przy którym BMI jest prawidłowe dla wybranego wzrostu.
wartości startowe: 170 cm i 65 kg
<Window x:Class="Lekcja19.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Kalkulator BMI" SizeToContent="Height" Width="460"
ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">
<StackPanel Margin="20">
<!-- Wzrost: krok 1 cm dzięki IsSnapToTickEnabled -->
<DockPanel>
<TextBlock x:Name="WzrostOpis" DockPanel.Dock="Right" Width="70"
TextAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="Wzrost"/>
</DockPanel>
<Slider x:Name="Wzrost" Minimum="120" Maximum="220" Value="170"
TickFrequency="1" IsSnapToTickEnabled="True"
SmallChange="1" LargeChange="5"
ValueChanged="Suwak_ValueChanged" Margin="0,4,0,16"/>
<!-- Waga: krok 0,5 kg dzięki IsSnapToTickEnabled -->
<DockPanel>
<TextBlock x:Name="WagaOpis" DockPanel.Dock="Right" Width="70"
TextAlignment="Right" FontWeight="Bold"/>
<TextBlock Text="Waga"/>
</DockPanel>
<Slider x:Name="Waga" Minimum="30" Maximum="150" Value="65"
TickFrequency="0.5" IsSnapToTickEnabled="True"
SmallChange="0.5" LargeChange="5"
ValueChanged="Suwak_ValueChanged" Margin="0,4,0,16"/>
<!-- Wynik w ramce, kolor zależny od kategorii -->
<Border x:Name="Karta" CornerRadius="8" Padding="12">
<StackPanel>
<TextBlock x:Name="Bmi" FontSize="30" FontWeight="Bold"/>
<TextBlock x:Name="Kategoria" FontSize="16"/>
<TextBlock x:Name="Zakres" Margin="0,6,0,0" TextWrapping="Wrap"/>
</StackPanel>
</Border>
<TextBlock Margin="0,10,0,0" FontSize="11" Foreground="Gray" TextWrapping="Wrap"
Text="BMI to tylko orientacyjny wskaźnik dla dorosłych. U nastolatków ocenia się go na siatkach centylowych."/>
</StackPanel>
</Window>
using System.Windows;
using System.Windows.Media;
namespace Lekcja19;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Przelicz();
}
// Oba suwaki obsługuje ta sama metoda
private void Suwak_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Przelicz();
}
private void Przelicz()
{
// ValueChanged przychodzi już przy ustawianiu Value w XAML
if (Karta == null || Zakres == null)
{
return;
}
double wzrostCm = Wzrost.Value;
double waga = Waga.Value;
double wzrostM = wzrostCm / 100;
double bmi = waga / (wzrostM * wzrostM);
WzrostOpis.Text = $"{wzrostCm:F0} cm";
WagaOpis.Text = $"{waga:F1} kg";
Bmi.Text = $"BMI {bmi:F1}";
if (bmi < 18.5)
{
Kategoria.Text = "niedowaga";
Karta.Background = Brushes.LightBlue;
}
else if (bmi < 25)
{
Kategoria.Text = "waga prawidłowa";
Karta.Background = Brushes.LightGreen;
}
else if (bmi < 30)
{
Kategoria.Text = "nadwaga";
Karta.Background = Brushes.Khaki;
}
else
{
Kategoria.Text = "otyłość";
Karta.Background = Brushes.LightSalmon;
}
// zakres wagi dla BMI 18,5–24,9 przy tym wzroście
double od = 18.5 * wzrostM * wzrostM;
double doWagi = 24.9 * wzrostM * wzrostM;
Zakres.Text = $"Przy wzroście {wzrostCm:F0} cm BMI prawidłowe daje waga od {od:F1} do {doWagi:F1} kg.";
}
}
Co dzieje się po kolei
- Nad każdym suwakiem stoi
DockPanel: wartość przyklejona do prawej, nazwa wypełnia resztę — nazwa jest przy lewej krawędzi, liczba przy prawej. TickFrequency+IsSnapToTickEnabledustawiają krok: 1 cm dla wzrostu i 0,5 kg dla wagi. Kresek nie widać, bo nie maTickPlacement.SmallChangeiLargeChangesprawiają, że strzałki i PgUp/PgDn zmieniają wartość o sensowny krok.- Oba suwaki mają tę samą metodę
ValueChanged, która tylko wywołujePrzelicz. Wartości odczytujemy zValueobu suwaków, a nie ze.NewValue, bo potrzebne są obie. Przeliczwychodzi od razu, gdy karta wyniku jeszcze nie istnieje —Valueustawione w XAML wywołuje zdarzenie w trakcie budowania okna. Konstruktor wywołujePrzeliczjeszcze raz, gdy wszystko jest gotowe.- BMI = waga / wzrost², gdzie wzrost jest w metrach. Dla 170 cm i 65 kg: 65 / 2,89 ≈ 22,5.
- Granice prawidłowej wagi to wzór odwrócony: waga = BMI × wzrost². Dla 170 cm: 18,5 × 2,89 ≈ 53,5 kg i 24,9 × 2,89 ≈ 72,0 kg.
- Kolor karty zmienia łańcuch
if/else if— progi sprawdzane są od najmniejszego.
Sprawdź trzy rzeczy
- Usuń
IsSnapToTickEnabled="True"przy wadze i przeciągnij uchwyt. Wartość wciąż ma jedno miejsce po przecinku w napisie, ale BMI zmienia się płynnie, bo liczy się z niezaokrąglonej wartości. - Dopisz do suwaka wzrostu
TickPlacement="BottomRight". Pod suwakiem pojawi się 100 kresek — gęsto. ZmieńTickFrequencyna 10 i zobacz, jak zmienił się krok. - Usuń z
Przeliczwarunekif (Karta == null …). Program zakończy się wyjątkiem przy starcie.
Zestawienie elementów
| Element | Typ | Co robi |
|---|---|---|
Minimum, Maximum | double | Zakres suwaka (domyślnie 0–10). |
Value | double | Bieżąca wartość, zawsze w zakresie. |
SmallChange, LargeChange | double | Krok strzałek / PgUp, PgDn i kliknięcia w pasek. |
TickFrequency | double | Odstęp kresek podziałki. |
IsSnapToTickEnabled | bool | Wartość przyskakuje do kresek. |
TickPlacement | None, TopLeft, BottomRight, Both | Gdzie rysować kreski. |
AutoToolTipPlacement | None, TopLeft, BottomRight | Dymek z wartością przy przeciąganiu. |
Orientation | Horizontal, Vertical | Suwak poziomy / pionowy. |
ValueChanged | zdarzenie | Zmiana wartości; parametr RoutedPropertyChangedEventArgs<double> z OldValue i NewValue. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
ValueChanged z parametrem RoutedEventArgs | Błąd: sygnatura metody nie pasuje. Typ: RoutedPropertyChangedEventArgs<double>. |
int x = Suwak.Value; | CS0266 — double nie zamieni się sam na int. (int)Suwak.Value. |
| napis „Wartość: 40,37192” | Brak kroku albo formatu. Dodaj IsSnapToTickEnabled lub {x:F0}. |
brak sprawdzania null w ValueChanged | NullReferenceException przy starcie, gdy Value jest w XAML. |
Minimum="10" Maximum="5" | Suwak przyjmuje maksimum równe minimum — nie da się go przesunąć. |
suwak pionowy bez wysokości w StackPanel | Ma bardzo małą wysokość. Daj mu Height albo postaw w wierszu *. |
Zadania
Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Przy każdym suwaku pokazuj jego wartość z jednostką. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.
Wróć do mieszalnika RGB z lekcji 06, ale zamiast przycisków użyj trzech suwaków R, G, B (0–255, krok 1) i czwartego A — przezroczystość (0–255). Duży prostokąt podglądu stoi na tle w szachownicę, żeby przezroczystość była widoczna. Pod podglądem kod koloru „#80FF8800” i przycisk Szary, który ustawia R, G i B na średnią z trzech suwaków.
pokaż wskazówkę
- Kolor z czterech składowych:
Color.FromArgb((byte)a, (byte)r, (byte)g, (byte)b)— rzutowanie zdoublenabyte. - Szachownicę zrobisz z
UniformGridpełnego szarych i białych ramek, a podgląd postawisz w tej samej komórceGridna wierzchu.
Suwak rozmiaru czcionki 8–72 (krok 1, kreski co 8) i obok pole tekstowe z liczbą. Przesuwanie suwaka zmienia liczbę w polu i rozmiar przykładowego tekstu pod spodem. Wpisanie liczby w pole (i Enter) przesuwa suwak; liczba spoza zakresu przyjmuje najbliższą granicę, a tekst zamiast liczby przywraca poprzednią wartość.
pokaż wskazówkę
- Przypisanie wartości spoza zakresu do
Valuesamo ją przytnie — wystarczy potem przepisaćValuez powrotem do pola. - Enter w polu: przycisk Ustaw z
IsDefault="True".
Pole Kwota rachunku, suwak Napiwek 0–25% z kreskami i krokiem co 5% oraz suwak Liczba osób 1–12. Na żywo: kwota napiwku, razem do zapłaty i kwota na osobę zaokrąglona w górę do 10 groszy. Pod spodem: „Zaokrąglenie dało 0,20 zł nadwyżki”. Przy błędnej kwocie zamiast wyników pokaż podpowiedź.
pokaż wskazówkę
- Licz na
decimal— wartości suwaków zamień przez(decimal)Suwak.Value. - Nadwyżka = kwota na osobę × liczba osób − razem.
Pionowy suwak temperatury 15–28 °C z krokiem 0,5 °C, obok duża liczba „21,5 °C”, której kolor zmienia się z niebieskiego (poniżej 19), przez zielony, do czerwonego (powyżej 24). Pole wyboru Tryb eko ogranicza maksimum do 21 °C — jeśli temperatura była wyższa, suwak sam zjeżdża do 21 i pojawia się informacja „Obniżono do 21 °C (tryb eko)”. Przyciski Noc (18 °C) i Dzień (21 °C) ustawiają gotowe wartości.
pokaż wskazówkę
- Pionowy suwak potrzebuje wysokości, np.
Height="200". Krok ustawiasz jak w przykładzie:TickFrequencyiIsSnapToTickEnabled. - Zmiana
Maximumz kodu sama przytnieValue; porównaj wartość przed i po, żeby wiedzieć, czy pokazać informację.
Uczeń planuje oszczędności: pole Cel (np. 2400 zł), suwak Miesięczna wpłata 50–1000 zł (co 50 zł) i suwak Liczba miesięcy 1–24. Program na żywo pokazuje, ile uzbiera, czy cel zostanie osiągnięty, a jeśli nie — ile miesięcy potrzeba przy tej wpłacie i jaka wpłata wystarczy w wybranym czasie (zaokrąglona w górę do 50 zł). Wynik w ramce zielonej albo pomarańczowej.
pokaż wskazówkę
- Potrzebne miesiące: dzielenie z zaokrągleniem w górę —
Math.Ceiling(cel / wplata). - Wpłata do 50 zł w górę:
Math.Ceiling(kwota / 50) * 50.
Pięć pionowych suwaków pasm (60 Hz, 230 Hz, 910 Hz, 3,6 kHz, 14 kHz), każdy od −12 do +12 dB z krokiem 1 i kreską na zerze, pod każdym wartość „+3 dB”. Nad suwakami lista rozwijana ustawień: Płaski, Bas, Wokal, Rock, Własny. Wybór ustawienia przesuwa wszystkie suwaki na wartości zapisane w tablicy. Gdy użytkownik ręcznie przesunie dowolny suwak, lista sama przełącza się na Własny (ale nie wtedy, gdy suwaki przesuwa wybór ustawienia). Przycisk Wyzeruj ustawia Płaski.
60 Hz
230 Hz
910 Hz
3,6 kHz
14 kHz
pokaż wskazówkę
- Ustawienia zapisz w tablicy
int[,](ustawienie × pasmo), a suwaki włóż do panelu i odczytuj przezChildren[i]. - Pole klasy
bool ustawiamZKodu: ustaw je natrueprzed przesuwaniem suwaków z listy i nafalsepo — wValueChangedprzełączaj listę na Własny tylko, gdy jestfalse.
Co trzeba zapamiętać
Sliderwybiera liczbę z zakresuMinimum–Maximum;Valuetodoublei zawsze mieści się w zakresie.- Krok =
TickFrequency+IsSnapToTickEnabled; kreski pokażeTickPlacement. ValueChangedma parametrRoutedPropertyChangedEventArgs<double>i przychodzi także przy starcie okna.- Wartość wyświetlaj z formatem i jednostką; do liczb całkowitych użyj
(int)lubMath.Round. - Suwak sam pilnuje zakresu — nie trzeba sprawdzać danych jak w polu tekstowym.
Dokumentacja: Microsoft Learn — klasa Slider.