Search code examples
androidxamarinmvvmcrosscountdown

Countdown MVVMCross with Xamarin Android


I want to create a countdown timer for my textview in my droid project in MVVMCross.

This is my solution : enter image description here

I have a little problem with this solution because i can't use System.Threading.Timer, so i need to create my own class.

But i don't know how, because, my own class must be in the Business Project and this class must be called with my .Core project with my UI Project...

Can you help me ?

Thank you

I made this, but not working : TimerCustom class in SelfieBox.Business

public class TimerCustom
{
    private int _valueOfTimer;
    private bool _timerRunning;

    public int ValueOfTimer
    {
        get { return _valueOfTimer; }
        set { _valueOfTimer = value; }
    }

    public TimerCustom(int valueOfTimer)
    {
        _valueOfTimer = valueOfTimer;
    }

    public async Task<TimerCustom> Start()
    {
        if (!_timerRunning)
        {
            _timerRunning = true;
            await RunTimer();
        }

        return this;
    }

    private async Task RunTimer()
    {
        while (_timerRunning && _valueOfTimer > 0)
        {
            await Task.Delay(1);

            _valueOfTimer--;

            if (_valueOfTimer == 0)
            {
                _timerRunning = false;
            }
        }
    }
}

ViewModel in SelfieBox.Core :

public class SecondStep_Photo_ViewModel : MvxViewModel
{
    private Business.Models.TimerCustom _timerCustom;

    #region Prop => Value Of Timer
    private int _valueOfTimer;

    public int ValueOfTimer
    {
        get { return _valueOfTimer; }
        set { _valueOfTimer = value; }
    }
    #endregion


    public SecondStep_Photo_ViewModel()
    {
        _timerCustom = new Business.Models.TimerCustom(5);
        _timerCustom.Start();

        ValueOfTimer = _timerCustom.ValueOfTimer;
    }
}

And my view, in SelfieBox.Droid :

...<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/TxtTimer"
    android:textSize="200dp"
    android:textStyle="bold"
    android:gravity="center"
    android:visible="false" 
    android:layout_centerInParent="true" 
    local:MvxBind="Text ValueOfTimer; Visible DisplayTimer"/>

My timer decreased nicely, but it is not displayed on my screen


Solution

  • The problem is that in your ViewModel, you never react to the changes in your timer and you never call RaisePropertyChanged either.

    So I would change your timer to something like this instead:

    public class Timer
    {
        private bool _started;
    
        public int Time { get; private set; }
        public event EventHandler<int> TimeElapsed;
    
        public Timer(int startTime)
        {
            Time = startTime;
        }
    
        public async Task StartAsync(CancellationToken token = default(CancellationToken))
        {
            if (_started) return;
    
            _started = true;
    
            while (_started && Time > 0)
            {
                // wait 1000 ms
                await Task.Delay(1000, token).ConfigureAwait(false);
                if (--Time == 0)
                {
                    _started = false;
                }
                TimeElapsed?.Invoke(this, Time);
            }
        }
    }
    

    This way you can in the event it exposes update your ViewModel accordingly:

    var timer = new Timer(5);
    timer.TimeElapsed += (s, t) => ValueOfTimer = t;
    timer.StartAsync();
    

    Make sure that ValueOfTimer raise PropertyChanged:

    private int _valueOfTimer;
    
    public int ValueOfTimer
    {
        get { return _valueOfTimer; }
        set { 
            _valueOfTimer = value;
            RaisePropertyChanged(() => ValueOfTimer);
        }
    }