Sunday, 17 August 2025

Creating a Digital Clock and Timer in .NET MAUI

Here's a complete implementation of a digital clock and timer in .NET MAUI. This solution includes both a real-time clock display and a countdown timer functionality.

1. Create the ViewModel

First, let's create a ViewModel to handle the time and timer logic:

csharp
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Timers;

public class ClockTimerViewModel : INotifyPropertyChanged
{
    private Timer _clockTimer;
    private Timer _countdownTimer;
    private TimeSpan _remainingTime;
    private bool _isTimerRunning;
    private string _timerButtonText = "Start";

    public event PropertyChangedEventHandler PropertyChanged;

    public DateTime CurrentTime => DateTime.Now;
    
    public TimeSpan RemainingTime
    {
        get => _remainingTime;
        set
        {
            if (_remainingTime != value)
            {
                _remainingTime = value;
                OnPropertyChanged();
                OnPropertyChanged(nameof(RemainingTimeDisplay));
            }
        }
    }
    
    public string RemainingTimeDisplay => RemainingTime.ToString(@"hh\:mm\:ss");
    
    public bool IsTimerRunning
    {
        get => _isTimerRunning;
        set
        {
            if (_isTimerRunning != value)
            {
                _isTimerRunning = value;
                OnPropertyChanged();
                TimerButtonText = _isTimerRunning ? "Stop" : "Start";
            }
        }
    }
    
    public string TimerButtonText
    {
        get => _timerButtonText;
        set
        {
            if (_timerButtonText != value)
            {
                _timerButtonText = value;
                OnPropertyChanged();
            }
        }
    }
    
    public int Hours { get; set; } = 0;
    public int Minutes { get; set; } = 0;
    public int Seconds { get; set; } = 0;

    public ClockTimerViewModel()
    {
        // Initialize clock timer (updates every second)
        _clockTimer = new Timer(1000);
        _clockTimer.Elapsed += (s, e) => OnPropertyChanged(nameof(CurrentTime));
        _clockTimer.Start();

        // Initialize countdown timer
        _countdownTimer = new Timer(1000);
        _countdownTimer.Elapsed += CountdownTimerElapsed;
    }

    private void CountdownTimerElapsed(object sender, ElapsedEventArgs e)
    {
        if (RemainingTime.TotalSeconds <= 0)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                IsTimerRunning = false;
                _countdownTimer.Stop();
                // You could add an alert here when timer completes
            });
            return;
        }

        RemainingTime = RemainingTime.Subtract(TimeSpan.FromSeconds(1));
    }

    public void StartStopTimer()
    {
        if (IsTimerRunning)
        StopTimer();
        else
        StartTimer();
    }

    private void StartTimer()
    {
        if (RemainingTime.TotalSeconds <= 0)
        {
            // Set initial time from pickers
            RemainingTime = new TimeSpan(Hours, Minutes, Seconds);
        }

        if (RemainingTime.TotalSeconds > 0)
        {
            IsTimerRunning = true;
            _countdownTimer.Start();
        }
    }

    private void StopTimer()
    {
        IsTimerRunning = false;
        _countdownTimer.Stop();
    }

    public void ResetTimer()
    {
        StopTimer();
        RemainingTime = new TimeSpan(Hours, Minutes, Seconds);
    }

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

2. Create the XAML View

Now, let's create the UI in XAML:

xml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodels="clr-namespace:YourNamespace.ViewModels"
             x:Class="YourNamespace.MainPage"
             Title="MAUI Clock & Timer">

    <ContentPage.BindingContext>
        <viewmodels:ClockTimerViewModel />
    </ContentPage.BindingContext>

    <ScrollView>
        <VerticalStackLayout Spacing="20" Padding="20">

            <!-- Digital Clock Section -->
            <Frame BorderColor="LightGray" CornerRadius="10" Padding="20">
                <VerticalStackLayout Spacing="10">
                    <Label Text="Digital Clock" FontSize="Title" HorizontalOptions="Center"/>
                    <Label Text="{Binding CurrentTime, StringFormat='{0:HH:mm:ss}'}" 
                           FontSize="48" 
                           HorizontalOptions="Center"
                           FontAttributes="Bold"/>
                    <Label Text="{Binding CurrentTime, StringFormat='{0:dddd, MMMM d, yyyy}'}" 
                           FontSize="16" 
                           HorizontalOptions="Center"/>
                </VerticalStackLayout>
            </Frame>

            <!-- Timer Section -->
            <Frame BorderColor="LightGray" CornerRadius="10" Padding="20">
                <VerticalStackLayout Spacing="15">
                    <Label Text="Timer" FontSize="Title" HorizontalOptions="Center"/>

                    <!-- Timer Display -->
                    <Label Text="{Binding RemainingTimeDisplay}" 
                           FontSize="48" 
                           HorizontalOptions="Center"
                           FontAttributes="Bold"/>

                    <!-- Time Input -->
                    <HorizontalStackLayout Spacing="10" HorizontalOptions="Center">
                        <Picker Title="H" ItemsSource="{Binding HourOptions}" 
                                SelectedIndex="{Binding Hours}" 
                                WidthRequest="80"/>
                        <Label Text=":" VerticalTextAlignment="Center"/>
                        <Picker Title="M" ItemsSource="{Binding MinuteSecondOptions}" 
                                SelectedIndex="{Binding Minutes}" 
                                WidthRequest="80"/>
                        <Label Text=":" VerticalTextAlignment="Center"/>
                        <Picker Title="S" ItemsSource="{Binding MinuteSecondOptions}" 
                                SelectedIndex="{Binding Seconds}" 
                                WidthRequest="80"/>
                    </HorizontalStackLayout>

                    <!-- Timer Controls -->
                    <HorizontalStackLayout Spacing="20" HorizontalOptions="Center">
                        <Button Text="{Binding TimerButtonText}" 
                                Command="{Binding StartStopTimerCommand}"
                                WidthRequest="100"/>
                        <Button Text="Reset" 
                                Command="{Binding ResetTimerCommand}"
                                WidthRequest="100"/>
                    </HorizontalStackLayout>
                </VerticalStackLayout>
            </Frame>

        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

3. Update the ViewModel with Commands and Options

Add these additional properties to your ViewModel:

csharp
public ClockTimerViewModel()
{
    // ... existing constructor code ...

    StartStopTimerCommand = new Command(StartStopTimer);
    ResetTimerCommand = new Command(ResetTimer);

    // Initialize picker options
    HourOptions = Enumerable.Range(0, 24).Select(i => i.ToString()).ToList();
    MinuteSecondOptions = Enumerable.Range(0, 60).Select(i => i.ToString()).ToList();
}

public ICommand StartStopTimerCommand { get; }
public ICommand ResetTimerCommand { get; }

public List<string> HourOptions { get; }
public List<string> MinuteSecondOptions { get; }

4. Add Platform-Specific Code for Android (Optional)

For Android, you might want to add this to your MauiProgram.cs to ensure timers work when the app is in the background:

csharp
// In your MauiProgram.CreateMauiApp() method
#if ANDROID
    Microsoft.Maui.Handlers.WidgetHandler.LayoutManager.AlwaysMeasure = true;
#endif

5. Complete Code-Behind

Here's the code-behind for the page (though it's mostly empty since we're using MVVM):

csharp
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

Features of This Implementation:

  1. Digital Clock:

    • Shows current time updating every second

    • Displays the current date

  2. Timer:

    • Countdown timer with hours, minutes, and seconds

    • Start/Stop and Reset functionality

    • Time selection via pickers

    • Visual display of remaining time

  3. MVVM Architecture:

    • Clean separation of concerns

    • Data binding for automatic UI updates

    • Commands for button actions

Customization Options:

  • You can change the fonts, colors, and sizes in the XAML

  • Add alarm sounds when the timer completes (use IAudio service)

  • Add vibration when timer completes (use IVibration service)

  • Add more timer presets

  • Implement multiple timers

This implementation provides a complete, functional digital clock and timer in .NET MAUI with a clean UI and proper separation of concerns using the MVVM pattern.

No comments:

Post a Comment

Complete Guide: Building a Live Cricket Streaming App for 100M Users

Comprehensive guide to building a scalable live cricket streaming platform for 100M users, covering backend infrastructure, streaming techno...