using System.Globalization; namespace ZegarProsty; // ============================================================================ // MainPage - jedyna strona aplikacji // ============================================================================ // Ta klasa to "code-behind" pliku MainPage.xaml - czyli logika C# powiązana // z konkretnym widokiem XAML. Metoda InitializeComponent() (wywoływana // w konstruktorze) jest generowana automatycznie przez kompilator na // podstawie MainPage.xaml i tworzy wszystkie kontrolki (GraphicsView, // Label, itd.) opisane w tamtym pliku, udostępniając je tutaj jako pola // (np. TarczaZegara, EtykietaCzas - nazwy wzięte z atrybutów x:Name w XAML). // ============================================================================ public partial class MainPage : ContentPage { // Instancja klasy odpowiedzialnej za rysowanie tarczy zegara. // Trzymamy ją jako pole, bo będziemy do niej co sekundę wpisywać // nowy czas (patrz OdswiezZegar poniżej). private readonly ClockDrawable _rysownikZegara = new(); // IDispatcherTimer to timer dostarczany przez samo MAUI (a nie przez // .NET ogólnie). Jego kluczowa zaleta: callback (Tick) wykonuje się // automatycznie na wątku UI, więc można w nim bezpiecznie zmieniać // teksty etykiet czy wywoływać Invalidate() na GraphicsView. // Gdyby użyć zwykłego System.Threading.Timer, zmiana UI z jego wątku // tła zakończyłaby się wyjątkiem na większości platform. private IDispatcherTimer? _timer; public MainPage() { InitializeComponent(); // Podłączamy nasz obiekt rysujący do kontrolki GraphicsView // zdefiniowanej w XAML pod nazwą "TarczaZegara". Od tego momentu // GraphicsView będzie wywoływać _rysownikZegara.Draw(...) za każdym // razem, gdy poprosimy o przerysowanie (metoda Invalidate()). TarczaZegara.Drawable = _rysownikZegara; UruchomTimer(); // Wywołujemy odświeżenie od razu przy starcie strony, żeby // użytkownik nie zobaczył pustego/domyślnego zegara przez // pierwszą sekundę oczekiwania na pierwszy "tick" timera. OdswiezZegar(); } // Tworzy i uruchamia timer odświeżający widok co 1 sekundę. private void UruchomTimer() { _timer = Dispatcher.CreateTimer(); _timer.Interval = TimeSpan.FromSeconds(1); // Wyrażenie lambda "(_, _) => OdswiezZegar()" to skrócony zapis // metody obsługującej zdarzenie Tick. Podkreślniki oznaczają, // że nie potrzebujemy parametrów zdarzenia (sender, EventArgs). _timer.Tick += (_, _) => OdswiezZegar(); _timer.Start(); } // Główna metoda wykonywana co sekundę: pobiera aktualny czas systemowy // i aktualizuje zarówno tekst (zegar cyfrowy), jak i każe przerysować // tarczę analogową. private void OdswiezZegar() { DateTime teraz = DateTime.Now; // Formatowanie godziny w stylu 24-godzinnym: HH = godzina 00-23, // mm = minuty, ss = sekundy. CultureInfo.InvariantCulture gwarantuje, // że separator (dwukropek) będzie taki sam niezależnie od // regionalnych ustawień systemu operacyjnego użytkownika. EtykietaCzas.Text = teraz.ToString("HH:mm:ss", CultureInfo.InvariantCulture); // Formatowanie pełnej daty po polsku: dddd = pełna nazwa dnia // tygodnia, d = dzień miesiąca, MMMM = pełna nazwa miesiąca, // yyyy = rok czterocyfrowy. Kultura "pl-PL" zapewnia polskie nazwy // ("poniedziałek", "stycznia" itd.) zamiast angielskich. var kulturaPolska = new CultureInfo("pl-PL"); string data = teraz.ToString("dddd, d MMMM yyyy", kulturaPolska); // .NET zwraca nazwę dnia tygodnia małą literą (np. "poniedziałek"), // więc ręcznie zamieniamy pierwszą literę na wielką dla ładniejszego // wyglądu zdania. EtykietaData.Text = char.ToUpper(data[0], kulturaPolska) + data[1..]; // Przekazujemy świeżo pobrany czas do obiektu rysującego... _rysownikZegara.AktualnyCzas = teraz; // ...i wymuszamy ponowne wywołanie metody Draw() na GraphicsView. // Bez tego wywołania tarcza zegara pozostałaby "zamrożona" na // ostatnio narysowanym obrazie, mimo że dane w środku się zmieniły. TarczaZegara.Invalidate(); } // OnDisappearing jest wywoływane automatycznie przez framework, gdy // strona znika z ekranu (np. użytkownik przełącza aplikację w tło). // Zatrzymujemy wtedy timer, żeby nie zużywać procesora i baterii // na odświeżanie widoku, którego i tak nikt nie widzi. protected override void OnDisappearing() { base.OnDisappearing(); _timer?.Stop(); } // Analogicznie: gdy strona znów staje się widoczna, wznawiamy timer. protected override void OnAppearing() { base.OnAppearing(); _timer?.Start(); } }