//JobTask.cs using System; using System.Threading; using System.Threading.Tasks; namespace TaskJobCreater { public class JobTask { CancellationTokenSource cancellationTokenSource { get; set; } Task task; private Task CreateTask(Action action, CancellationToken cancellationToken) { return new Task(() => { while (!cancellationTokenSource.Token.IsCancellationRequested) { if (StartDate < DateTime.Now) { IsStarted = true; Started.Invoke(this); action(); Completed(); return; } task.Wait(1000); } }, cancellationTokenSource.Token ); } public DateTime StartDate { get; private set; } public void Start(DateTime startDate, Action action) { if (IsActive) throw new InvalidOperationException("Task started before"); StartDate = startDate; cancellationTokenSource = new CancellationTokenSource(); task = CreateTask(action, cancellationTokenSource.Token); task.Start(); IsActive = true; if (Activated != null) Activated.Invoke(this); } public event Action<object> Stopped; public event Action<object> Started; public event Action<object> Activated; private void Completed() { IsActive = false; IsStarted = false; Stopped.Invoke(this); } public void Stop() { if (IsActive) { cancellationTokenSource.Cancel(); try { task.Wait(); } catch (OperationCanceledException ce) { task.Dispose(); } Completed(); } } public bool IsActive { get; private set; } public bool IsStarted { get; private set; } } } /****************************************************************/ //MainWindowModel.cs using System; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; namespace TaskJobCreater { public class MainWindowModel : INotifyPropertyChanged { public MainWindowModel() { job.Stopped += (s) => { Log("Job has ben stopped"); StartJob(); }; job.Started += (s) => { Log($"Job has been started"); ButtonName = "START"; }; job.Activated += (s) => { Log( $"Job will start after {Convert.ToInt32((job.StartDate - DateTime.Now).TotalSeconds)} seconds later."); ButtonName = "STOP"; }; StartJob(); canExecute = true; } private bool canExecute; private ICommand clickCommand; public ICommand ClickCommand => clickCommand ?? (clickCommand = new CommandHandler(() => ToogleJob(), canExecute)); public void ToogleJob() { if (job.IsActive) StopJob(); else StartJob(); } private void Log(string log) { Logs += log; Logs += Environment.NewLine; } private void StartJob() => job.Start(DateTime.Now.AddSeconds(5), Job); private void StopJob() => job.Stop(); public void Job() => Log(DateTime.Now.ToString("HH:mm:ss")); string m_ButtonName = "START"; public string ButtonName { get { return m_ButtonName; } set { m_ButtonName = value; OnPropertyChanged(); } } string m_Logs; public string Logs { get { return m_Logs; } set { m_Logs = value; OnPropertyChanged(); } } public JobTask job = new JobTask(); #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName]string propertyName = "") { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion } } /****************************************************************/ //CommandHandler.cs using System; using System.Windows.Input; namespace TaskJobCreater { public class CommandHandler : ICommand { private Action action; private bool canExecute; public CommandHandler(Action action, bool canExecute) { this.action = action; this.canExecute = canExecute; } public bool CanExecute(object parameter) => canExecute; public event EventHandler CanExecuteChanged; public void Execute(object parameter) => action(); } } /****************************************************************/ //MainWindow.xaml <Window x:Class="TaskJobCreater.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:TaskJobCreater" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Button Content="{Binding ButtonName}" Height="40" Command="{Binding ClickCommand}"/> <TextBox Grid.Row="1" Text="{Binding Logs}" AcceptsReturn="True" /> </Grid> </Window> /****************************************************************/ //MainWindow.cs using System.Windows; namespace TaskJobCreater { ////// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public readonly MainWindowModel model; public MainWindow() { InitializeComponent(); model = new MainWindowModel(); DataContext = model; } } }