How to auto close the time in a custom view?

mc 3,641 Reputation points
2021-03-10T09:24:17.453+00:00

in xamarin.form I created a custom view and add a time of Device.StartTimer

how to close it when the page disappeared?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,292 questions
0 comments No comments
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 68,261 Reputation points Microsoft Vendor
    2021-03-11T09:08:50.037+00:00

    it all depends the value which I set to it.

    If you want to add a binding property to control the timer, you can achieve it like following code in custom view.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace App64
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class View1 : ContentView
        {
    
           // public static MyTimer myTimer;
            public static DeviceTimer deviceTimer;
            public int TimeWait = 1;
            public View1()
            {
                InitializeComponent();
                //myTimer = new MyTimer(new TimeSpan(1), () => {
    
                //    TimeWait++;
                //    mylabel.Text = TimeWait.ToString();
                //});
    
                //myTimer.Start();
    
                deviceTimer = new DeviceTimer( () => {
    
                      TimeWait++;
                     mylabel.Text = TimeWait.ToString();
                    }, new TimeSpan(1),true,true);
    
            }
    
            public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(View1), propertyChanged: OnEventNameChanged);
    
            private static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
            {
    
    
                 if(newValue.Equals("stop"))
                {
                    deviceTimer.Stop();
                }
                else if (newValue.Equals("Restart"))
                {
                    deviceTimer.Restart();
                }
            }
    
            public string EventName
            {
                get { return (string)GetValue(EventNameProperty); }
                set { SetValue(EventNameProperty, value); }
            }
        }
    }
    

    Here is customview's forground code.

    <?xml version="1.0" encoding="UTF-8"?>
    <ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="App64.View1">
      <ContentView.Content>
          <StackLayout>
              <Label x:Name="mylabel" Text="Hello Xamarin.Forms!" />
    
    
          </StackLayout>
      </ContentView.Content>
    </ContentView
    

    When we use it in the contentpage we can use Button to make a test.

    <app64:View1 EventName="{Binding MyEvent}" >
                        <app64:View1.Effects>
                            <app64:ViewLifecycleEffect Loaded="ViewLifecycleEffect_Loaded" Unloaded="ViewLifecycleEffect_Unloaded"/>
                        </app64:View1.Effects>
    
                    </app64:View1>
    
                    <Button Text="navi" Clicked="Button_Clicked"></Button>
                    <Button Text="stop" Clicked="Button_Clicked_1"/>
                    <Button Text="restart" Clicked="Button_Clicked_2"/>
    

    background code.

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    namespace App64
    {
        public partial class MainPage : ContentPage
        {
            string myEvent;
            public string MyEvent
            {
                set
                {
                    if (myEvent != value)
                    {
                        myEvent = value;
                        OnPropertyChanged("MyEvent");
    
                    }
                }
                get
                {
                    return myEvent;
                }
            }
            public MainPage()
            {
                InitializeComponent();
                BindingContext = this;
            }
    
            protected override void OnAppearing()
            {
    
                base.OnAppearing();
                View1.deviceTimer.Restart();
            }
    
            protected override void OnDisappearing()
            {
                base.OnDisappearing();
                View1.deviceTimer.Stop();
                //   View1.myTimer.Stop();
            }
    
    
            private void Button_Clicked_1(object sender, EventArgs e)
            {
                MyEvent = "stop";
            }
    
            private void Button_Clicked_2(object sender, EventArgs e)
            {
                MyEvent = "Restart";
            }
    
    
            private void ViewLifecycleEffect_Loaded(object sender, EventArgs e)
            {
                DisplayAlert("INFO","SHOWING","OK");
            }
    
            private void ViewLifecycleEffect_Unloaded(object sender, EventArgs e)
            {
                DisplayAlert("INFO", "disappear", "OK");
            }
    
            private void Button_Clicked(object sender, EventArgs e)
            {
                Navigation.PushAsync(new Page1());
            }
        }
    }
    
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 68,261 Reputation points Microsoft Vendor
    2021-03-10T10:51:08.49+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    Firstly, you can create a custom Timer like following code. You can control the Timer to start or stop by yourself.

       using System;  
       using System.Collections.Generic;  
       using System.Text;  
       using System.Threading;  
       using Xamarin.Forms;  
    
       namespace App64  
       {  
           public  class MyTimer  
           {  
               private readonly TimeSpan timespan;  
               private readonly Action callback;  
    
               private CancellationTokenSource cancellation;  
    
               public MyTimer(TimeSpan timespan, Action callback)  
               {  
                   this.timespan = timespan;  
                   this.callback = callback;  
                   this.cancellation = new CancellationTokenSource();  
               }  
    
               public void Start()  
               {  
                   CancellationTokenSource cts = this.cancellation; // safe copy  
                   Device.StartTimer(this.timespan,  
                       () => {  
                           if (cts.IsCancellationRequested) return false;  
                           this.callback.Invoke();  
                           return false; // or true for periodic behavior  
                       });  
               }  
    
               public void Stop()  
               {  
                   Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();  
               }  
           }  
       }  
    

    Then, you can use MyTimer in your customview and I set it to static.

       public partial class View1 : ContentView  
           {  
    
               public static MyTimer myTimer;  
    
               public int TimeWait = 1;  
               public View1()  
               {  
                   InitializeComponent();  
                   myTimer = new MyTimer(new TimeSpan(1), () => {  
    
                       TimeWait++;  
                       mylabel.Text = TimeWait.ToString();  
                   });  
    
                   myTimer.Start();  
               }  
    
    
           }  
       }  
    

    If you want to close it when the page disappeared?

    You can stop the timer in the OnDisappearing method of ContentPage.

       protected override void OnDisappearing()  
               {  
                   base.OnDisappearing();  
    
                   View1.myTimer.Stop();  
               }  
    

    ========================
    Update=========================

    What I want it when the view disappeared the timer stop but when the view is appeared the timer can resolve the origin state.

    for example.

    If the timer is running when the page is created and the page disappeared and re -appeared then the timer will running.

    If the timer is not running when the page is created and the page disappeared and re-appeared then the timer will not running

    it all depends the value which I set to it.

    I think the stop and the start should not be set and will auto change. if the page disappear it will stop if the view appeared it will keep the state which is set in the cs code.

    First of all, if you create a custom-view, View do not have lifecycle method(such as OnAppearing or OnDisappearing like ContentPage). So you think the stop and the start should not be set and will auto change. It cannot be achieved.

    If the page disappear it will stop if the view appeared it will keep the state

    It could be achieved, we can create a new custom Timer like following code.

       using System;  
       using System.Collections.Generic;  
       using System.Linq;  
       using System.Text;  
       using System.Threading;  
       using Xamarin.Forms;  
    
       namespace App64  
       {  
    
           public class DeviceTimer  
           {  
               readonly Action _Task;  
               readonly List<TaskWrapper> _Tasks = new List<TaskWrapper>();  
               readonly TimeSpan _interval;  
               public bool IsRecurring { get; }  
               public bool IsRunning => _Tasks.Any(t => t.IsRunning);  
    
               public DeviceTimer(Action task, TimeSpan interval,  
                 bool isRecurring = false, bool start = false)  
               {  
                   _Task = task;  
                   _interval = interval;  
                   IsRecurring = isRecurring;  
                   if (start)  
                       Start();  
               }  
    
               public void Restart()  
               {  
                   Stop();  
                   Start();  
               }  
    
               public void Start()  
               {  
                   if (IsRunning)  
                       // Already Running  
                       return;  
    
                   var wrapper = new TaskWrapper(_Task, IsRecurring, true);  
                   _Tasks.Add(wrapper);  
    
                   Device.StartTimer(_interval, wrapper.RunTask);  
               }  
    
               public void Stop()  
               {  
                   foreach (var task in _Tasks)  
                       task.IsRunning = false;  
                   _Tasks.Clear();  
               }  
    
    
               class TaskWrapper  
               {  
                   public bool IsRunning { get; set; }  
                   bool _IsRecurring;  
                   Action _Task;  
                   public TaskWrapper(Action task, bool isRecurring, bool isRunning)  
                   {  
                       _Task = task;  
                       _IsRecurring = isRecurring;  
                       IsRunning = isRunning;  
                   }  
    
                   public bool RunTask()  
                   {  
                       if (IsRunning)  
                       {  
                           _Task();  
                           if (_IsRecurring)  
                               return true;  
                       }  
    
                       // No longer need to recur. Stop  
                       return IsRunning = false;  
                   }  
               }  
           }  
    

    Then use it in the custom view.

       public partial class View1 : ContentView  
           {  
    
    
               public static DeviceTimer deviceTimer;  
               public int TimeWait = 1;  
               public View1()  
               {  
                   InitializeComponent();  
    
    
                   deviceTimer = new DeviceTimer( () => {  
    
                         TimeWait++;  
                        mylabel.Text = TimeWait.ToString();  
                       }, new TimeSpan(1),true,true);  
    
               }  
    

    Then we can control it in the ContentPage.

       public MainPage()  
               {  
                   InitializeComponent();  
                //   BindingContext = this;  
               }  
    
               protected override void OnAppearing()  
               {  
    
                   base.OnAppearing();  
                   View1.deviceTimer.Restart();  
               }  
    
               protected override void OnDisappearing()  
               {  
                   base.OnDisappearing();  
                   View1.deviceTimer.Stop();  
                   //   View1.myTimer.Stop();  
               }  
    

    Best Regards,

    Leon Lu


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.