Kontrolka Slider, ProgressBar i mikser kolorów RGB

Jak pozwolić użytkownikowi wybrać wartość liczbową z przedziału od 0 do 255 (np. jasność ekranu, głośność muzyki lub składową koloru RGB), mając 100% pewności, że nie wpisze tam ujemnej liczby, liter ani wartości 9999?

Odpowiedzią jest kontrolka Slider (suwak). To jeden z absolutnych ulubieńców komisji egzaminacyjnej CKE - pojawia się w zadaniach z wzornikiem kolorów RGB, generatorem haseł czy zmianą rozmiaru czcionki. Zobaczmy, jak go zaprogramować bez potknięć!

1. Anatomia kontrolki Slider: Koniec z ułamkami!

Gdy wrzucisz domyślny Slider do okna i przesuniesz go myszką, jego właściwość Value zwróci liczbę zmiennoprzecinkową typu double, np. 47.3829104. W większości zadań egzaminacyjnych potrzebujemy jednak czystych liczb całkowitych.

Aby zmusić suwak do skakania wyłącznie po pełnych liczbach, w XAML ustawiamy zestaw czterech właściwości:

Właściwość XAML Do czego służy? Przykład dla RGB
Minimum Wartość minimalna (lewy skraj suwaka). Minimum="0"
Maximum Wartość maksymalna (prawy skraj suwaka). Maximum="255"
Value Początkowa pozycja suwaka. Value="255"
TickFrequency Co ile jednostek rozmieszczone są podziałki (kroki). TickFrequency="1"
IsSnapToTickEnabled Kluczowe! Przyciąga suwak dokładnie do podziałek (eliminuje ułamki!). IsSnapToTickEnabled="True"
<Slider x:Name="slCzerwony" 
        Minimum="0" 
        Maximum="255" 
        Value="128" 
        TickFrequency="1" 
        IsSnapToTickEnabled="True"
        ValueChanged="SuwakRGB_ValueChanged" />

2. ProgressBar: Wizualny brat suwaka

Kontrolka ProgressBar (pasek postępu) posiada dokładnie te same właściwości zakresowe co suwak (Minimum, Maximum, Value), ale jest tylko do odczytu - użytkownik nie może jej przesuwać myszką.

<ProgressBar x:Name="pbPostep" Minimum="0" Maximum="100" Value="65" Height="20" />

Świetnie sprawdza się do wizualizacji zapełnienia dysku, siły hasła lub postępu ładowania.

3. Zdarzenie ValueChanged i mikser kolorów RGB w C#

Aby aplikacja reagowała płynnie podczas przeciągania suwaka, podpinamy zdarzenie ValueChanged.

W zadaniach egzaminacyjnych bardzo często musimy na podstawie trzech suwaków (Red, Green, Blue) pokolorować tło wybranego elementu (np. Rectangle, Border lub StackPanel). W WPF do nadania koloru używamy pędzla SolidColorBrush oraz struktury Color.FromRgb:

Kod C#
using System.Windows.Media; // Wymagane dla SolidColorBrush i Color!

private void SuwakRGB_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    // 1. STRAŻNIK: Chroni przed błędem podczas uruchamiania okna!
    if (slR == null || slG == null || slB == null || rectWzornik == null) 
        return;

    // 2. Pobieramy wartości z suwaków i rzutujemy z double na byte (zakres 0-255)
    byte r = (byte)slR.Value;
    byte g = (byte)slG.Value;
    byte b = (byte)slB.Value;

    // 3. Aktualizujemy etykiety tekstowe obok suwaków
    lblR.Text = r.ToString();
    lblG.Text = g.ToString();
    lblB.Text = b.ToString();

    // 4. Malujemy tło kontrolki nowym kolorem RGB
    rectWzornik.Background = new SolidColorBrush(Color.FromRgb(r, g, b));
}

🧠 Dlaczego na początku metody potrzebny jest „Strażnik” (if (slR == null...) return)?
Gdy okno startuje i wykonuje metodę InitializeComponent(), WPF czyta Twój plik XAML linijka po linijce. Kiedy natrafi na pierwszy suwak slR z ustawionym Value="255", natychmiast odpala zdarzenie ValueChanged - zanim jeszcze zdąży utworzyć suwaki slG i slB znajdujące się niżej w kodzie! Linijka sprawdzająca null chroni program przed wyłożeniem się przy starcie.

4. Zobaczmy to w akcji: Kompletny wzornik RGB

Oto gotowy układ XAML dla klasycznego zadania z mieszaniem barw:

XAML
<StackPanel Margin="20">
    <!-- Duży podgląd mieszanego koloru -->
    <Border x:Name="rectWzornik" Height="100" Background="Black" BorderBrush="Gray" BorderThickness="1" Margin="0,0,0,20" />

    <!-- Suwak R (Red) -->
    <DockPanel Margin="0,0,0,10">
        <TextBlock Text="R:" Width="25" FontWeight="Bold" />
        <TextBlock x:Name="lblR" Text="0" Width="35" DockPanel.Dock="Right" TextAlignment="Right" />
        <Slider x:Name="slR" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True" ValueChanged="SuwakRGB_ValueChanged" />
    </DockPanel>

    <!-- Suwak G (Green) -->
    <DockPanel Margin="0,0,0,10">
        <TextBlock Text="G:" Width="25" FontWeight="Bold" />
        <TextBlock x:Name="lblG" Text="0" Width="35" DockPanel.Dock="Right" TextAlignment="Right" />
        <Slider x:Name="slG" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True" ValueChanged="SuwakRGB_ValueChanged" />
    </DockPanel>

    <!-- Suwak B (Blue) -->
    <DockPanel Margin="0,0,0,10">
        <TextBlock Text="B:" Width="25" FontWeight="Bold" />
        <TextBlock x:Name="lblB" Text="0" Width="35" DockPanel.Dock="Right" TextAlignment="Right" />
        <Slider x:Name="slB" Minimum="0" Maximum="255" TickFrequency="1" IsSnapToTickEnabled="True" ValueChanged="SuwakRGB_ValueChanged" />
    </DockPanel>
</StackPanel>