Przejdź do głównej treści
INF.04-01-23.06-SGCzerwiec 2023 · 28 kroków · ok. 79 min · 30 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja konsolowa i desktopowa (C# + WPF)

Rozwiązujemy arkusz z sitem Eratostenesa i okienkiem do nadawania przesyłek w wersji C# + WPF: w konsoli pseudokod z arkusza przepisany na funkcję wykreślającą wielokrotności w tablicy logicznej, a w aplikacji desktopowej okno „Nadaj Przesyłkę” z grupą przycisków opcji, obrazem i ceną zależnymi od rodzaju przesyłki oraz sprawdzaniem kodu pocztowego. Do tego dokumentacja funkcji według listingu 1, zrzuty i dokument egzamin.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się cytatem wymagania, a kod pokazuje dokładnie te linie, które je spełniają. Najpierw piszemy najprostszą wersję, której wymaga klucz, a w etapie „Dobre praktyki” poprawiamy to, czego klucz nie sprawdza.

Co widać w podglądzie. Wyniki programu konsolowego pochodzą z prawdziwego uruchomienia kodu z danego kroku (.NET 10), a wypisane liczby porównaliśmy z niezależnie wyznaczoną listą 25 liczb pierwszych do 100. Okno aplikacji to symulacja WPF w przeglądarce zbudowana z XAML danego kroku, z obrazami z archiwum materialy.zip; jej zachowanie odpowiada kodowi C# z kroku. Każdy stan okna sprawdziliśmy na prawdziwym WPF - obraz, cenę i wszystkie trzy komunikaty.

Arkusz pozwala też na C++, Javę albo Pythona i na inne biblioteki okienkowe - ten wariant to jedna z możliwych dróg.

Jak liczą się punkty. Klucz ma 30 kryteriów (R.1.1 - R.4.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 23 punkty - można stracić 7 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Przygotowanie

    Krok 1 · po tym etapie masz 0 z 30 kryteriów (pkt)

    Krok 1: Folder zdającego i trzy podfoldery

    ok. 2 min
    Polecenie z arkusza

    Utwórz folder i nazwij go numerem zdającego. W folderze utwórz podfoldery: konsola, desktop, dokumentacja.

    Na pulpicie zakładamy folder o nazwie z numerem PESEL, a w nim konsola, desktop i dokumentacja. Zrzuty ekranu i dokument egzamin trafią do folderu dokumentacja.

  2. Etap 2: Aplikacja konsolowa

    Kroki 2-7 · po tym etapie masz 11 z 30 kryteriów (pkt)

    Krok 2: Projekt konsola w C#

    ok. 3 min
    Polecenie z arkusza

    Program wykonywany w konsoli. Język programowania zgodny z zainstalowanym na stanowisku egzaminacyjnym, jeden z: C++, C#, Java, Python.

    Wybieramy C#. W Visual Studio: Utwórz nowy projekt → Aplikacja konsolowa, nazwa konsola; w terminalu: dotnet new console -n konsola. W Program.cs zostawiamy klasę Program z pustą metodą Main.

    konsola/Program.cs C# nowy plik
    class Program
    {
        static void Main()
        {
        }
    }
  3. Krok 3: Funkcja FindPrimes: tablica jako argument, bez wartości zwracanej

    Kryteria: , , , ,

    R.2.1 Program składa się z programu głównego oraz funkcji lub metody. Zawartość funkcji jest zgodna z jej przeznaczeniem. Dopuszcza się niedokończoną funkcję, ale wskazującą na wypełnienie tablicy

    R.2.2 Utworzono funkcję lub metodę z argumentem: zmienna typu tablicowego oraz funkcja nie zwraca wartości (np. typ void)

    R.1.3 Zastosowano znaczące nazewnictwo funkcji

    R.1.4 Zastosowano znaczące nazewnictwo zmiennych. Wyjątkami od reguły są zmienne bufor, tmp, iteratory pętli itp. Kryterium nie jest spełnione tylko wtedy, gdy nazwy zmiennych nic nie znaczą, np. x, a, tab, tablica, fun; dopuszcza się aby tablica była nazwana A - zgodnie z pseudokodem

    R.1.5 Zastosowano typy zmiennych pasujące do problemu (np. typ tablicowy jednowymiarowy, typ całkowity dla iteratora pętli) w przypadku Python, tam gdzie jest to wymagane zastosowano jawną konwersję do odpowiednich typów

    ok. 3 min
    Polecenie z arkusza

    Wypełnianie tablicy odbywa się w osobnej funkcji przyjmującej tablicę jako argument i nie zwracającej żadnej wartości.

    Funkcja typu void (nie zwraca wartości) przyjmuje tablicę bool[] - jedna wartość logiczna na każdą liczbę (R.2.2, R.1.5). Tablica w C# jest przekazywana przez referencję: funkcja zmienia tę samą tablicę, którą utworzy Main, więc nie musi niczego zwracać.

    Pierwszy wiersz pseudokodu: tablica „początkowo wypełniona wartościami true” od indeksu 2 (indeksy 0 i 1 pomijamy). Nazwa isPrime („czy liczba jest pierwsza”) mówi, co znaczy true w danej komórce (R.1.4).

    konsola/Program.cs C# zmienione: 8 linii
    class Program
    {
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
        }
    
        static void Main()
        {
        }
    }
  4. Krok 4: Program główny: tablica do 100 i wypisanie

    Kryteria:

    R.1.7 Program po uruchomieniu wyświetla liczby oddzielone dowolnym separatorem oraz wyświetla znaczący komunikat. Jeżeli kod nie uruchamia się z powodu błędów kompilacji - sprawdzić w kodzie aplikacji

    ok. 3 min
    Polecenie z arkusza

    Program szuka liczb w przedziale 2..100 (n = 100). Liczby pierwsze są wyświetlane na ekranie, rozdzielone dowolnym separatorem oraz poprzedzone znaczącym komunikatem.

    Tablica ma n + 1 elementów - indeksy od 0 do 100, żeby liczba 100 miała swoją komórkę. Main wywołuje funkcję, a potem wypisuje komunikat i każdy indeks, w którym jest true, oddzielając liczby spacją (R.1.7).

    Podgląd: funkcja na razie tylko wypełnia tablicę, więc wypisują się wszystkie liczby od 2 do 100. Wykreślanie dopiszemy w dwóch kolejnych krokach.

    konsola/Program.cs C# zmienione: 13 linii
    class Program
    {
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
        }
    
        static void Main()
        {
            int n = 100;
            bool[] isPrime = new bool[n + 1];
            FindPrimes(isPrime);
    
            Console.Write("Liczby pierwsze z przedziału 2.." + n + ": ");
            for (int i = 2; i <= n; i++)
            {
                if (isPrime[i])
                {
                    Console.Write(i + " ");
                }
            }
            Console.WriteLine();
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczby pierwsze z przedziału 2..100: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  5. Krok 5: Pętla zewnętrzna do pierwiastka z n

    Kryteria: ,

    R.2.3 Zastosowano algorytm sita Eratostenesa. Pętla zewnętrzna działa na indeksach od 2 do 10

    R.1.2 Kod zapisano z wcięciami dla zagłębień bloków

    ok. 3 min
    Polecenie z arkusza

    for i := 2, 3, 4, ..., nie więcej niż √n: if A[i] = true. Wykreślanie powtarzamy do momentu, gdy liczba i, której wielokrotność wykreślamy, będzie większa niż √n.

    Pętla zewnętrzna przechodzi od 2 do pierwiastka z n. Zamiast liczyć pierwiastek (Math.Sqrt zwraca liczbę rzeczywistą) porównujemy i * i <= n - to ten sam warunek na liczbach całkowitych. Dla n = 100 pętla działa dla i od 2 do 10 (R.2.3). n w funkcji to ostatni indeks tablicy: isPrime.Length - 1.

    Warunek if (isPrime[i]) to drugi wiersz pseudokodu - wielokrotności wykreślamy tylko dla liczb jeszcze niewykreślonych. Wnętrze uzupełnimy w następnym kroku.

    konsola/Program.cs C# zmienione: 7 linii
    class Program
    {
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
    
            for (int i = 2; i * i <= isPrime.Length - 1; i++)
            {
                if (isPrime[i])
                {
                }
            }
        }
    
        static void Main()
        {
            int n = 100;
            bool[] isPrime = new bool[n + 1];
            FindPrimes(isPrime);
    
            Console.Write("Liczby pierwsze z przedziału 2.." + n + ": ");
            for (int i = 2; i <= n; i++)
            {
                if (isPrime[i])
                {
                    Console.Write(i + " ");
                }
            }
            Console.WriteLine();
        }
    }

    Wynik programu bez zmian - taki jak po kroku 4.

  6. Krok 6: Pętla wewnętrzna: wykreślanie wielokrotności

    Kryteria: , ,

    R.2.4 Zapisano pętlę wewnętrzną, która przypisuje wartości false dla indeksów będących wielokrotnością zmiennej sterującej pętlą zewnętrzną

    R.2.5 Program uruchamia się w konsoli, co udokumentowano zrzutem ekranu oraz wypisuje tylko liczby pierwsze: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97

    R.1.1 Kod źródłowy zapisano w sposób czytelny: instrukcje w osobnych liniach, stosowane spacje pomiędzy operatorami, konsekwentnie stosowana wybrana konwencja dla nawiasów klamrowych instrukcji blokowej

    ok. 3 min
    Polecenie z arkusza

    for j := 2i, 3i, 4*i, ..., nie więcej niż n: A[j] := false. Wyjście: wartości i takie, że A[i] zawiera wartość true.

    Pętla wewnętrzna zaczyna od 2 * i i skacze co i (j += i) - przechodzi dokładnie przez wielokrotności i większe od niego samego i każdą oznacza false (R.2.4).

    Podgląd to prawdziwe uruchomienie: zostaje 25 liczb pierwszych od 2 do 97 - te same, które wymienia klucz (R.2.5).

    konsola/Program.cs C# zmienione: 4 linie
    class Program
    {
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
    
            for (int i = 2; i * i <= isPrime.Length - 1; i++)
            {
                if (isPrime[i])
                {
                    for (int j = 2 * i; j <= isPrime.Length - 1; j += i)
                    {
                        isPrime[j] = false;
                    }
                }
            }
        }
    
        static void Main()
        {
            int n = 100;
            bool[] isPrime = new bool[n + 1];
            FindPrimes(isPrime);
    
            Console.Write("Liczby pierwsze z przedziału 2.." + n + ": ");
            for (int i = 2; i <= n; i++)
            {
                if (isPrime[i])
                {
                    Console.Write(i + " ");
                }
            }
            Console.WriteLine();
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczby pierwsze z przedziału 2..100: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
  7. Krok 7: Folder konsola: archiwum, źródło i plik wykonywalny

    ok. 2 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W podfolderze konsola powinno znaleźć się archiwum całego projektu o nazwie konsola.zip, plik z kodem źródłowym programu oraz plik wykonywalny, jeżeli istnieje.

    Folder projektu pakujemy do konsola.zip, obok kopiujemy Program.cs i konsola.exe z bin\Debug\net.... Archiwum robimy na końcu, po dopisaniu nagłówka funkcji z części III.

    Wynik programu bez zmian - taki jak po kroku 6.

  8. Etap 3: Aplikacja desktopowa

    Kroki 8-21 · po tym etapie masz 21 z 30 kryteriów (pkt)

    Krok 8: Nowy projekt WPF desktop i obrazy z archiwum

    ok. 3 min
    Polecenie z arkusza

    Za pomocą środowiska programistycznego dostępnego na stanowisku egzaminacyjnym, wykonaj aplikację desktopową będącą fragmentem aplikacji do obsługi poczty. Wykorzystaj umieszczone na pulpicie materiały o nazwie materialy.zip zabezpieczone hasłem: %Twoj@Paczka&

    W Visual Studio: Utwórz nowy projekt → Aplikacja WPF, nazwa desktop. Wygląd okna piszemy w MainWindow.xaml, logikę w MainWindow.xaml.cs. Archiwum materialy.zip rozpakowujemy hasłem z arkusza - są w nim trzy obrazy: pocztowka.png, list.png i paczka.png. Dołączymy je do projektu za chwilę.

    desktop/MainWindow.xaml XAML nowy plik
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
    
        </Grid>
    </Window>
    desktop/MainWindow.xaml.cs C# nowy plik
    using System.Windows;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    desktop/desktop.csproj Projekt C# nowy plik
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net10.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
    </Project>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  9. Krok 9: Tytuł okna i szerokość dopasowana do kontrolek

    Kryteria:

    R.3.1 Oknu aplikacji nadano nazwę "Nadaj Przesyłkę" wraz z numerem zdającego. Szerokość okna jest dopasowana do kontrolek wewnątrz

    ok. 2 min
    Polecenie z arkusza

    Okno dialogowe o nazwie „Nadaj Przesyłkę” i szerokości dopasowanej do kontrolek wewnątrz. W nazwie okna należy wstawić także numerem zdającego.

    Tytuł to „Nadaj Przesyłkę” i numer zdającego - na egzaminie numer PESEL, tu stoi Bartosz Bryniarski. Zamiast sztywnych Width i Height ustawiamy SizeToContent="WidthAndHeight" - WPF dopasuje rozmiar okna do zawartości, którą zaraz dodamy (R.3.1). Puste okno ma na razie minimalny rozmiar.

    desktop/MainWindow.xaml XAML zmienione: 1 linia
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid>
    
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  10. Krok 10: Grupa Rodzaj przesyłki z polami radio

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    R.3.6 Aplikacja uruchamia się, co udokumentowano zrzutem ekranu, układ kontrolek jest zgodny z Obrazem 1, stan początkowy to: zaznaczone pole Pocztówka, wyświetlony obraz pocztowka.png , pola radio są zgrupowane i jednocześnie można zaznaczyć tylko jedno z nich

    ok. 3 min
    Polecenie z arkusza

    Grupa pól radio: Pocztówka, List, Paczka; pola są zgrupowane w dowolny dostępny element grupujący (np. GroupBox), w stanie początkowym zaznaczone jest pole Pocztówka. Pola radio działają w grupie, jednocześnie może być wybrane tylko jedno pole.

    Okno dzielimy Grid-em na dwie kolumny (lewa na przesyłkę, prawa na adres) i dwa wiersze (na dole przycisk „Zatwierdź”). Kolumny mają stałe szerokości - od nich SizeToContent liczy szerokość okna.

    W lewej kolumnie ramka GroupBox z podpisem i trzy RadioButton - w WPF przyciski opcji w jednym kontenerze tworzą jedną grupę, więc zaznaczenie jednego odznacza pozostałe. IsChecked="True" przy „Pocztówka” to stan początkowy (R.3.6). Klikaj opcje w podglądzie.

    Typowe błędy

    CheckBox zamiast RadioButton pozwala zaznaczyć kilka rodzajów naraz.

    desktop/MainWindow.xaml XAML zmienione: 18 linii
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
            </StackPanel>
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  11. Krok 11: Grupa Dane adresowe: trzy pola z etykietami

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    ok. 3 min
    Polecenie z arkusza

    Trzy pola edycyjne poprzedzone etykietami o treści „Ulica z numerem”, „Kod pocztowy”, „Miasto”; zgrupowane w dowolny dostępny element grupujący.

    Prawa kolumna (Grid.Column="1"): drugi GroupBox, a w nim pionowy StackPanel z parami etykieta - pole. Pole kodu jest węższe, jak na obrazie 1. Każde pole ma nazwę; z postalCodeTextBox skorzystamy przy zatwierdzaniu.

    desktop/MainWindow.xaml XAML zmienione: 10 linii
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  12. Krok 12: Przycisk Sprawdź Cenę

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    ok. 1 min
    Polecenie z arkusza

    Przycisk o treści „Sprawdź Cenę”.

    Przycisk pod grupą przesyłek, tej samej szerokości co ramka. Obsługę kliknięcia dopiszemy po ułożeniu wszystkich kontrolek.

    desktop/MainWindow.xaml XAML zmienione: 1 linia
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0" />
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  13. Krok 13: Obraz pocztowka.png z zasobów aplikacji

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    R.3.6 Aplikacja uruchamia się, co udokumentowano zrzutem ekranu, układ kontrolek jest zgodny z Obrazem 1, stan początkowy to: zaznaczone pole Pocztówka, wyświetlony obraz pocztowka.png , pola radio są zgrupowane i jednocześnie można zaznaczyć tylko jedno z nich

    ok. 3 min
    Polecenie z arkusza

    Obraz w stanie początkowym wyświetlający obraz pocztowka.png. Pliki obrazów zapisane w zasobach aplikacji.

    Trzy obrazy z archiwum kopiujemy do folderu projektu i oznaczamy jako zasoby (w Visual Studio: Właściwości pliku → Akcja kompilacji: Resource; w pliku projektu <Resource Include="*.png" />). Zasób zostaje wbudowany w plik .exe - aplikacja nie szuka obrazów na dysku i działa po skopiowaniu w inne miejsce.

    Kontrolka Image z Source="pocztowka.png" pokazuje obraz pocztówki od razu po starcie (R.3.6). Obraz ma nazwę parcelImage - będziemy go podmieniać w kodzie.

    Typowe błędy

    Obraz dodany do projektu z akcją None nie trafia do aplikacji - okno pokazuje pustą kontrolkę.

    desktop/MainWindow.xaml XAML zmienione: 3 linie
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0" />
                <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                    <Image x:Name="parcelImage" Source="pocztowka.png" Width="110" />
                </StackPanel>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
        </Grid>
    </Window>
    desktop/desktop.csproj Projekt C# zmienione: 4 linie
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net10.0-windows</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <Resource Include="*.png" />
      </ItemGroup>
    
    </Project>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  14. Krok 14: Etykieta Cena pogrubiona i większa

    Kryteria:

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    ok. 2 min
    Polecenie z arkusza

    Etykieta o treści „Cena: ”, o cechach: napis pogrubiony i czcionka widocznie większa względem pozostałych napisów.

    Label obok obrazu: FontWeight="Bold" pogrubia, a FontSize="16" powiększa napis - domyślny rozmiar czcionki w WPF to 12 (R.3.2). Nazwa priceLabel pozwoli wpisać cenę z kodu.

    desktop/MainWindow.xaml XAML zmienione: 1 linia
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0" />
                <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                    <Image x:Name="parcelImage" Source="pocztowka.png" Width="110" />
                    <Label x:Name="priceLabel" Content="Cena: " FontWeight="Bold" FontSize="16" Margin="20,10,0,0" />
                </StackPanel>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  15. Krok 15: Przycisk Zatwierdź na całą szerokość

    Kryteria: ,

    R.3.2 Okno zawiera wszystkie kontrolki widoczne na Obrazie 1 arkusza egzaminacyjnego, etykieta Cena: jest pogrubiona i napisana większą czcionką niż pozostałe napisy

    R.3.6 Aplikacja uruchamia się, co udokumentowano zrzutem ekranu, układ kontrolek jest zgodny z Obrazem 1, stan początkowy to: zaznaczone pole Pocztówka, wyświetlony obraz pocztowka.png , pola radio są zgrupowane i jednocześnie można zaznaczyć tylko jedno z nich

    ok. 2 min
    Polecenie z arkusza

    Przycisk o treści: „Zatwierdź”.

    Drugi wiersz siatki; Grid.ColumnSpan="2" rozciąga przycisk na obie kolumny, jak na obrazie 1. Wszystkie kontrolki są na miejscu - porównaj podgląd z obrazem 1.

    desktop/MainWindow.xaml XAML zmienione: 1 linia
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0" />
                <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                    <Image x:Name="parcelImage" Source="pocztowka.png" Width="110" />
                    <Label x:Name="priceLabel" Content="Cena: " FontWeight="Bold" FontSize="16" Margin="20,10,0,0" />
                </StackPanel>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
            <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Zatwierdź" Height="24" Margin="0,15,0,0" />
        </Grid>
    </Window>
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  16. Krok 16: Sprawdź Cenę: pocztówka

    Kryteria: ,

    R.3.3 Po wciśnięciu przynajmniej jednego przycisku wywołana jest funkcja lub metoda

    R.3.4 W funkcji / metodzie obsługującej sprawdzanie ceny pobrano stan przycisków typu radio i zastosowano instrukcję podejmującą akcję w zależności od wyboru (wystarczy instrukcja pobrania oraz zapisany dowolny zgodny ze składnią warunek / wybór)

    ok. 3 min
    Polecenie z arkusza

    Po wybraniu przycisku „Sprawdź cenę” aplikacja sprawdza, które pole radio jest zaznaczone i zależnie od wyboru wyświetla odpowiedni dla pola obraz oraz cenę, odpowiednio: pole radio Pocztówka: obraz pocztowka.png, „Cena: 1 zł”.

    W XAML wiążemy kliknięcie z metodą CheckPriceButton_Click (R.3.3). W metodzie pytamy przycisk opcji, czy jest zaznaczony: IsChecked ma typ bool? (może być też null), dlatego porównujemy z true (R.3.4).

    Obraz zmieniamy, przypisując do Source nowy BitmapImage z adresem pliku z zasobów - adres względny "pocztowka.png" (UriKind.Relative) wskazuje zasób w aplikacji. Cenę wpisujemy do Content etykiety. Kliknij „Sprawdź Cenę” przy zaznaczonej pocztówce.

    desktop/MainWindow.xaml XAML zmienione: 2 linie
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0"
                        Click="CheckPriceButton_Click" />
                <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                    <Image x:Name="parcelImage" Source="pocztowka.png" Width="110" />
                    <Label x:Name="priceLabel" Content="Cena: " FontWeight="Bold" FontSize="16" Margin="20,10,0,0" />
                </StackPanel>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
            <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Zatwierdź" Height="24" Margin="0,15,0,0" />
        </Grid>
    </Window>
    desktop/MainWindow.xaml.cs C# zmienione: 10 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("pocztowka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1 zł";
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  17. Krok 17: Sprawdź Cenę: list

    Kryteria: ,

    R.3.7 Po wybraniu przycisku do sprawdzania ceny, aplikacja wyświetla napisy: „Cena: 1 zł” dla pocztówki, „Cena: 1,5 zł” dla listu, „Cena: 10 zł” dla paczki

    R.3.8 Po wybraniu przycisku do sprawdzania ceny, aplikacja wyświetla odpowiednio obrazy: pocztowka.png , list.png, paczka.png

    ok. 2 min
    Polecenie z arkusza

    Pole radio List: obraz list.png, „Cena: 1,5 zł”.

    Kolejna gałąź else if - sprawdzana tylko wtedy, gdy pocztówka nie jest zaznaczona. Cenę piszemy z przecinkiem, tak jak w arkuszu: to tekst etykiety, nie liczba. Zaznacz w podglądzie „List” i kliknij „Sprawdź Cenę” - obraz 1.

    desktop/MainWindow.xaml.cs C# zmienione: 5 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("pocztowka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1 zł";
            }
            else if (letterRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("list.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1,5 zł";
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  18. Krok 18: Sprawdź Cenę: paczka

    Kryteria: ,

    R.3.7 Po wybraniu przycisku do sprawdzania ceny, aplikacja wyświetla napisy: „Cena: 1 zł” dla pocztówki, „Cena: 1,5 zł” dla listu, „Cena: 10 zł” dla paczki

    R.3.8 Po wybraniu przycisku do sprawdzania ceny, aplikacja wyświetla odpowiednio obrazy: pocztowka.png , list.png, paczka.png

    ok. 2 min
    Polecenie z arkusza

    Pole radio Paczka: obraz paczka.png, „Cena: 10 zł”.

    Trzecia gałąź dla paczki. Teraz każdy wybór daje swój obraz i cenę (R.3.7, R.3.8). Sprawdź w podglądzie wszystkie trzy opcje.

    desktop/MainWindow.xaml.cs C# zmienione: 5 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("pocztowka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1 zł";
            }
            else if (letterRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("list.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1,5 zł";
            }
            else if (parcelRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("paczka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 10 zł";
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  19. Krok 19: Zatwierdź: kod pocztowy musi mieć 5 znaków

    Kryteria: , ,

    R.3.3 Po wciśnięciu przynajmniej jednego przycisku wywołana jest funkcja lub metoda

    R.3.5 W funkcji / metodzie obsługującej zatwierdzenie okna pobrano wartość z pola kodu pocztowego oraz zaimplementowano sprawdzenie czy w polu wpisano 5 znaków za pomocą odpowiedniej instrukcji

    R.3.9 Po zatwierdzeniu okna dialogowego, gdy kod pocztowy składa się z pięciu cyfr wyświetlany jest komunikat „Dane przesyłki zostały wprowadzone”, gdy jest mniej lub więcej niż 5 znaków - „Nieprawidłowa liczba cyfr w kodzie pocztowym”

    ok. 3 min
    Polecenie z arkusza

    Po wybraniu przycisku „Zatwierdź” jest walidowane pole kodu pocztowego oraz wyświetlany komunikat. Dla uproszczenia zadania należy przyjąć, że kod składa się tylko z 5 cyfr (bez znaku '-'). Komunikat dla poprawnego kodu pocztowego: „Dane przesyłki zostały wprowadzone”. Komunikat, gdy jest mniej lub więcej niż 5 znaków: „Nieprawidłowa liczba cyfr w kodzie pocztowym”.

    Pobieramy tekst z pola kodu do zmiennej postalCode i sprawdzamy jego długość - Length to liczba znaków (R.3.5). Komunikat pokazuje MessageBox.Show. Wpisz w podglądzie 651 i kliknij „Zatwierdź”, potem 65124.

    Na razie każdy napis z pięciu znaków uznajemy za poprawny - także „65a24”. Sprawdzenie cyfr dopisujemy w następnym kroku.

    desktop/MainWindow.xaml XAML zmienione: 2 linie
    <Window x:Class="desktop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:desktop"
            mc:Ignorable="d"
            Title="Nadaj Przesyłkę, Bartosz Bryniarski" SizeToContent="WidthAndHeight">
        <Grid Margin="40,10,40,10">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="230" />
                <ColumnDefinition Width="310" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel>
                <GroupBox Header="Rodzaj przesyłki" Width="205" HorizontalAlignment="Left">
                    <StackPanel Margin="10,5">
                        <RadioButton x:Name="postcardRadioButton" Content="Pocztówka" IsChecked="True" Margin="0,5" />
                        <RadioButton x:Name="letterRadioButton" Content="List" Margin="0,5" />
                        <RadioButton x:Name="parcelRadioButton" Content="Paczka" Margin="0,5" />
                    </StackPanel>
                </GroupBox>
                <Button Content="Sprawdź Cenę" Width="205" HorizontalAlignment="Left" Margin="0,8,0,0"
                        Click="CheckPriceButton_Click" />
                <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
                    <Image x:Name="parcelImage" Source="pocztowka.png" Width="110" />
                    <Label x:Name="priceLabel" Content="Cena: " FontWeight="Bold" FontSize="16" Margin="20,10,0,0" />
                </StackPanel>
            </StackPanel>
            <GroupBox Grid.Column="1" Header="Dane adresowe">
                <StackPanel Margin="10,5">
                    <Label Content="Ulica z numerem" />
                    <TextBox x:Name="streetTextBox" Height="24" />
                    <Label Content="Kod pocztowy" Margin="0,8,0,0" />
                    <TextBox x:Name="postalCodeTextBox" Height="24" Width="115" HorizontalAlignment="Left" />
                    <Label Content="Miasto" Margin="0,8,0,0" />
                    <TextBox x:Name="cityTextBox" Height="24" Margin="0,0,0,10" />
                </StackPanel>
            </GroupBox>
            <Button Grid.Row="1" Grid.ColumnSpan="2" Content="Zatwierdź" Height="24" Margin="0,15,0,0"
                    Click="ConfirmButton_Click" />
        </Grid>
    </Window>
    desktop/MainWindow.xaml.cs C# zmienione: 13 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("pocztowka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1 zł";
            }
            else if (letterRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("list.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1,5 zł";
            }
            else if (parcelRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("paczka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 10 zł";
            }
        }
    
        private void ConfirmButton_Click(object sender, RoutedEventArgs e)
        {
            string postalCode = postalCodeTextBox.Text;
            if (postalCode.Length != 5)
            {
                MessageBox.Show("Nieprawidłowa liczba cyfr w kodzie pocztowym");
            }
            else
            {
                MessageBox.Show("Dane przesyłki zostały wprowadzone");
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  20. Krok 20: Zatwierdź: kod pocztowy z samych cyfr

    Kryteria: ,

    R.3.9 Po zatwierdzeniu okna dialogowego, gdy kod pocztowy składa się z pięciu cyfr wyświetlany jest komunikat „Dane przesyłki zostały wprowadzone”, gdy jest mniej lub więcej niż 5 znaków - „Nieprawidłowa liczba cyfr w kodzie pocztowym”

    R.3.10 Po zatwierdzeniu okna dialogowego, gdy przynajmniej jeden znak nie jest cyfrą „Kod pocztowy powinien się składać z samych cyfr”

    ok. 3 min
    Polecenie z arkusza

    Komunikat, gdy przynajmniej jeden znak nie jest cyfrą: „Kod pocztowy powinien się składać z samych cyfr”.

    Pętla foreach przechodzi przez każdy znak kodu; char.IsDigit odpowiada, czy znak jest cyfrą. Wystarczy jeden znak niebędący cyfrą, żeby flaga onlyDigits zmieniła się na false (R.3.10).

    Sprawdzamy w kolejności z arkusza: najpierw długość, potem cyfry. Przetestuj w podglądzie 651, 65a24 i 65124 - trzy różne komunikaty (R.3.9, R.3.10).

    desktop/MainWindow.xaml.cs C# zmienione: 17 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("pocztowka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1 zł";
            }
            else if (letterRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("list.png", UriKind.Relative));
                priceLabel.Content = "Cena: 1,5 zł";
            }
            else if (parcelRadioButton.IsChecked == true)
            {
                parcelImage.Source = new BitmapImage(new Uri("paczka.png", UriKind.Relative));
                priceLabel.Content = "Cena: 10 zł";
            }
        }
    
        private void ConfirmButton_Click(object sender, RoutedEventArgs e)
        {
            string postalCode = postalCodeTextBox.Text;
            if (postalCode.Length != 5)
            {
                MessageBox.Show("Nieprawidłowa liczba cyfr w kodzie pocztowym");
            }
            else
            {
                bool onlyDigits = true;
                foreach (char sign in postalCode)
                {
                    if (!char.IsDigit(sign))
                    {
                        onlyDigits = false;
                    }
                }
    
                if (onlyDigits)
                {
                    MessageBox.Show("Dane przesyłki zostały wprowadzone");
                }
                else
                {
                    MessageBox.Show("Kod pocztowy powinien się składać z samych cyfr");
                }
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  21. Krok 21: Folder desktop: archiwum i pliki źródłowe

    ok. 2 min
    Polecenie z arkusza

    Kod aplikacji przygotuj do nagrania na płytę. W podfolderze desktop powinno znaleźć się archiwum całego projektu o nazwie desktop.zip oraz plik (lub pliki) z kodem źródłowym modyfikowanym w czasie egzaminu.

    Pakujemy folder projektu (z obrazami) do desktop.zip, obok kopiujemy MainWindow.xaml, MainWindow.xaml.cs i desktop.csproj - zmieniony o zasoby.

    Okno aplikacji bez zmian - taki jak po kroku 20.

  22. Etap 4: Dokumentacja aplikacji

    Kroki 22-25 · po tym etapie masz 30 z 30 kryteriów (pkt)

    Krok 22: Nagłówek funkcji FindPrimes według listingu 1

    Kryteria: , , , ,

    R.4.1 Zapisano nagłówek dla funkcji z aplikacji konsolowej, utworzony w postaci komentarza zgodny z Listingiem 1 z arkusza egzaminacyjnego, umieszczony w komentarzu wieloliniowym lub kilku jednoliniowych

    R.4.2 W komentarzu ujęto nazwę funkcji

    R.4.3 W komentarzu ujęto nazwę i opis parametru wejściowego funkcji

    R.4.4 W komentarzu zapisano opis wartości zwracanej oraz w informacjach zapisano np. "wyznaczenie liczb pierwszych, sito Eratostenesa" lub podobne zapisy

    R.4.5 W komentarzu ujęto numer zdającego

    ok. 4 min
    Polecenie z arkusza

    W kodzie źródłowym aplikacji konsolowej utwórz nagłówek funkcji wypełniającej tablicę, według wzoru. Nagłówek powinien znaleźć się w kodzie źródłowym nad funkcją. W miejscu nawiasów <> należy podać nazwę funkcji, nazwy parametrów (lub słowo „brak”) oraz zwięzłe informacje (co realizuje funkcja?) – zgodnie z listingiem 1. W miejscu autor należy podać numer zdającego.

    Komentarz wieloliniowy /* ... */ nad funkcją, z polami listingu 1 w tej samej kolejności (R.4.1): nazwa funkcji (R.4.2), parametr isPrime z opisem, co przechowuje (R.4.3), wartość zwracana - „brak”, bo funkcja jest typu void - i informacje o sicie Eratostenesa (R.4.4), autor (R.4.5). Na egzaminie w polu autor wpisujemy numer PESEL - tu stoi Bartosz Bryniarski. Komentarz nie zmienia działania programu.

    konsola/Program.cs C# zmienione: 10 linii
    class Program
    {
        /*
        *******************************************************
         nazwa funkcji:       FindPrimes
         parametry wejściowe: isPrime - tablica logiczna; po wykonaniu funkcji isPrime[i] ma wartość
                              true, gdy liczba i jest pierwsza (indeksy od 2 do n)
         wartość zwracana:    brak - funkcja wypełnia przekazaną tablicę
         informacje:          wyznacza liczby pierwsze z przedziału 2..n algorytmem sita Eratostenesa
         autor:               Bartosz Bryniarski
        *****************************************************
        */
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
    
            for (int i = 2; i * i <= isPrime.Length - 1; i++)
            {
                if (isPrime[i])
                {
                    for (int j = 2 * i; j <= isPrime.Length - 1; j += i)
                    {
                        isPrime[j] = false;
                    }
                }
            }
        }
    
        static void Main()
        {
            int n = 100;
            bool[] isPrime = new bool[n + 1];
            FindPrimes(isPrime);
    
            Console.Write("Liczby pierwsze z przedziału 2.." + n + ": ");
            for (int i = 2; i <= n; i++)
            {
                if (isPrime[i])
                {
                    Console.Write(i + " ");
                }
            }
            Console.WriteLine();
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczby pierwsze z przedziału 2..100: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
  23. Krok 23: Zrzut konsola1

    Kryteria: ,

    R.1.6 Podjęto próbę uruchomienia kodu, co udokumentowano obrazem przedstawiającym wykonywany program lub próbę jego uruchomienia

    R.4.6 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji konsolowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzuty ekranu dokumentujące uruchomienie aplikacji utworzonych podczas egzaminu. Zrzuty powinny obejmować cały obszar ekranu z widocznym paskiem zadań. Dowolna liczba zrzutów ekranowych z aplikacji konsolowej nazwane konsola1, konsola2 itd.

    Uruchamiamy program z Visual Studio i robimy zrzut całego ekranu z oknem konsoli i środowiskiem - konsola1.png w folderze dokumentacja (R.1.6, R.4.6). Program nie wczytuje danych, więc jeden zrzut pokazuje całe jego działanie.

    Wynik programu bez zmian - taki jak po kroku 22.

  24. Krok 24: Zrzuty desktop1 - desktop6

    Kryteria:

    R.4.7 Zapisano przynajmniej jeden zrzut ekranu z uruchomienia lub kompilacji aplikacji desktopowej, na zrzucie widoczne jest środowisko, w którym powstała aplikacja lub okno z kompilowaną aplikacją

    ok. 4 min
    Polecenie z arkusza

    Dowolna liczba zrzutów ekranowych z aplikacji desktopowej nazwane desktop1, desktop2 itd.

    Zrzuty całego ekranu ze środowiskiem: desktop1 - stan początkowy, desktop2 - wybrany „List” i „Sprawdź Cenę” (obraz 1), desktop3 - „Paczka”, desktop4 i desktop5 - komunikaty o złej liczbie znaków i o znaku spoza cyfr, desktop6 - poprawny kod (R.4.7). Podgląd obok to aplikacja z poprzedniego etapu.

    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  25. Krok 25: Dokument egzamin

    Kryteria:

    R.4.8 W dokumentacji zapisano: nazwę systemu operacyjnego, nazwy użytych przez zdającego środowisk, nazwy języków programowania

    ok. 4 min
    Polecenie z arkusza

    W edytorze tekstu pakietu biurowego utwórz plik z dokumentacją i nazwij go egzamin. Dokument powinien zawierać podpisane zrzuty ekranu, a następnie zapisane informacje: nazwę systemu operacyjnego, na którym pracował zdający, nazwy środowisk programistycznych, z których zdający korzystał na egzaminie, nazwy języków programowania użytych podczas tworzenia aplikacji, opcjonalnie komentarz do wykonanej pracy.

    W Wordzie albo Writerze dokument egzamin w folderze dokumentacja: najpierw wklejone zrzuty, każdy z podpisem („desktop2 - wybrany list i sprawdzona cena”), potem trzy informacje o narzędziach (R.4.8). Plik obok to treść tekstowa dokumentu.

    egzamin.txt Tekst nowy plik
    Zrzuty ekranu: konsola1 - wynik programu konsolowego; desktop1 - desktop6 - stany aplikacji desktopowej
    System operacyjny: Windows 11
    Środowisko programistyczne: Visual Studio 2022 (aplikacja konsolowa i desktopowa)
    Języki programowania: C# (aplikacja konsolowa), C# i XAML - WPF (aplikacja desktopowa)

    Okno aplikacji bez zmian - taki jak po kroku 24.

  26. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 26-27 · po tym etapie masz 30 z 30 kryteriów (pkt)

    Krok 26: Konsola: stała n i lista bez separatora na końcu

    ok. 3 min
    Polecenie z arkusza

    Liczby pierwsze są wyświetlane na ekranie, rozdzielone dowolnym separatorem oraz poprzedzone znaczącym komunikatem.

    Dwie drobne poprawki:

    • const int n = 100 - granica przedziału nie zmienia się w trakcie programu, więc oznaczamy ją jako stałą; kompilator nie pozwoli jej przypadkiem nadpisać,
    • liczby zbieramy do listy List<int> i łączymy string.Join(", ", primes) - przecinek stoi tylko między liczbami, bez separatora po ostatniej, a komunikat i wynik wypisujemy jednym napisem interpolowanym $"...".

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    konsola/Program.cs C# zmienione: 6 linii
    class Program
    {
        /*
        *******************************************************
         nazwa funkcji:       FindPrimes
         parametry wejściowe: isPrime - tablica logiczna; po wykonaniu funkcji isPrime[i] ma wartość
                              true, gdy liczba i jest pierwsza (indeksy od 2 do n)
         wartość zwracana:    brak - funkcja wypełnia przekazaną tablicę
         informacje:          wyznacza liczby pierwsze z przedziału 2..n algorytmem sita Eratostenesa
         autor:               Bartosz Bryniarski
        *****************************************************
        */
        static void FindPrimes(bool[] isPrime)
        {
            for (int i = 2; i < isPrime.Length; i++)
            {
                isPrime[i] = true;
            }
    
            for (int i = 2; i * i <= isPrime.Length - 1; i++)
            {
                if (isPrime[i])
                {
                    for (int j = 2 * i; j <= isPrime.Length - 1; j += i)
                    {
                        isPrime[j] = false;
                    }
                }
            }
        }
    
        static void Main()
        {
            const int n = 100;
            bool[] isPrime = new bool[n + 1];
            FindPrimes(isPrime);
    
            List<int> primes = new List<int>();
            for (int i = 2; i <= n; i++)
            {
                if (isPrime[i])
                {
                    primes.Add(i);
                }
            }
            Console.WriteLine($"Liczby pierwsze z przedziału 2..{n}: {string.Join(", ", primes)}");
            // Console.Write("Liczby pierwsze z przedziału 2.." + n + ": ");
            // for (...) Console.Write(i + " ");
        }
    }
    Wynik programu po tym kroku
    > konsola.exe
    Liczby pierwsze z przedziału 2..100: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97
  27. Krok 27: Okno: jedna metoda do obrazu i ceny, cyfry 0-9

    ok. 4 min
    Polecenie z arkusza

    Aplikacja powinna być zapisana czytelnie, z zachowaniem zasad czystego formatowania kodu, należy stosować znaczące nazwy zmiennych i funkcji

    Trzy poprawki, które skracają kod i zamykają drobne luki:

    • trzy gałęzie powtarzały te same dwie linie - wyciągamy je do metody ShowParcel(obraz, cena), a gałęzie tylko ją wywołują; zmiana sposobu wyświetlania ceny to teraz jedno miejsce,
    • Trim() usuwa przypadkową spację przed albo po kodzie - „65124 ” nie jest już „złą liczbą cyfr”,
    • char.IsAsciiDigit przepuszcza wyłącznie cyfry 0-9; char.IsDigit uznaje za cyfry także np. cyfry arabskie. postalCode.All(char.IsAsciiDigit) (metoda LINQ) sprawdza warunek dla każdego znaku i zastępuje pętlę z flagą.

    Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    desktop/MainWindow.xaml.cs C# zmienione: 11 linii
    using System.Windows;
    using System.Windows.Media.Imaging;
    
    namespace desktop;
    
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void CheckPriceButton_Click(object sender, RoutedEventArgs e)
        {
            if (postcardRadioButton.IsChecked == true)
            {
                ShowParcel("pocztowka.png", "1 zł");
            }
            else if (letterRadioButton.IsChecked == true)
            {
                ShowParcel("list.png", "1,5 zł");
            }
            else if (parcelRadioButton.IsChecked == true)
            {
                ShowParcel("paczka.png", "10 zł");
            }
        }
    
        private void ShowParcel(string imageName, string price)
        {
            parcelImage.Source = new BitmapImage(new Uri(imageName, UriKind.Relative));
            priceLabel.Content = "Cena: " + price;
        }
    
        private void ConfirmButton_Click(object sender, RoutedEventArgs e)
        {
            string postalCode = postalCodeTextBox.Text.Trim();
            if (postalCode.Length != 5)
            {
                MessageBox.Show("Nieprawidłowa liczba cyfr w kodzie pocztowym");
            }
            else
            {
                if (postalCode.All(char.IsAsciiDigit))
                {
                    MessageBox.Show("Dane przesyłki zostały wprowadzone");
                }
                else
                {
                    MessageBox.Show("Kod pocztowy powinien się składać z samych cyfr");
                }
            }
        }
    }
    Okno aplikacji po tym kroku

    Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.

  28. Etap 6: Oddanie pracy

    Krok 28 · po tym etapie masz 30 z 30 kryteriów (pkt)

    Krok 28: Nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Nagraj płytę z rezultatami pracy. W folderze z numerem zdającego powinny się znajdować podfoldery: konsola, desktop, dokumentacja. Po nagraniu płyty sprawdź poprawność nagrania.

    Sprawdzamy zawartość przed nagraniem:

    • dokumentacja: konsola1.png, desktop1.png - desktop6.png, egzamin,
    • konsola: konsola.zip (spakowany po dopisaniu nagłówka funkcji), Program.cs, konsola.exe,
    • desktop: desktop.zip z obrazami, MainWindow.xaml, MainWindow.xaml.cs, desktop.csproj.

    Nagrywamy, sprawdzamy odczyt płyty, opisujemy ją numerem zdającego.

    Okno aplikacji bez zmian - taki jak po kroku 27.

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony). Treść arkusza i klucza oceniania pochodzi z CKE.