Hi Sarah,
try following demo. In this demo is only one event for one Report:
<Window x:Class="WpfApp1.Window024"
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:WpfApp024"
mc:Ignorable="d"
Title="Window024" Height="450" Width="800">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding View}"/>
</Grid>
</Window>
----------
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading;
using System.Windows;
using System.Xml;
using System.Xml.Serialization;
namespace WpfApp024
{
public class ViewModel
{
/// <summary>
/// constructor: initiate object for checking next time
/// </summary>
public ViewModel()
{
TimeModel tm = new TimeModel();
tm.NextReport += Tm_NextReport;
}
/// <summary>
/// Action to execute time event
/// </summary>
private void Tm_NextReport(object? sender, EventArgs e) => View.Add(DateTime.UtcNow.ToLongTimeString());
/// <summary>
/// for demo purposes
/// </summary>
public ObservableCollection<string> View { get; set; } = new ObservableCollection<string>();
}
public class TimeModel : IDisposable
{
public event EventHandler<EventArgs> NextReport;
private SynchronizationContext? sc = SynchronizationContext.Current;
Timer t = null;
public TimeModel() => NextEvent();
private void NextEvent()
{
DateTime nextTime = NextTimeData.NextTime;
if (nextTime < DateTime.UtcNow) RaiseEvent(null);
else
{
double waitTime1 = nextTime.Subtract(DateTime.UtcNow).TotalMilliseconds;
int waitTime2 = 24 * 3600 * 1000; // wait 1 day maximum
if (waitTime1 < waitTime2) waitTime2 = (int)waitTime1;
t = new Timer(RaiseEvent, t, waitTime2, Timeout.Infinite);
}
}
private void RaiseEvent(object? state)
{
var t = state as Timer;
if (t != null) t.Dispose(); // stop timer if exists
DateTime nextTime = NextTimeData.NextTime;
double waitTime1 = nextTime.Subtract(DateTime.UtcNow).TotalMilliseconds;
if (waitTime1 <= 0)
{
sc?.Send(new SendOrPostCallback((o) => NextReport?.Invoke(this, new EventArgs())), null);
NextTimeData.NextTime = GetNextTime();
}
NextEvent();
}
private DateTime GetNextTime()
{
var dt = DateTime.UtcNow;
return dt.AddSeconds(3);
}
private TimeData _nextTimeData;
private TimeData NextTimeData
{
get
{
if (_nextTimeData == null)
{
_nextTimeData = new TimeData() { NextTime = new DateTime(1, 1, 1) };
if (File.Exists(SavedTimePath))
using (XmlReader rdr = XmlReader.Create(SavedTimePath)) _nextTimeData = (TimeData)(new XmlSerializer(typeof(TimeData))).Deserialize(rdr);
}
return _nextTimeData;
}
set
{
_nextTimeData = value;
using (XmlWriter wrt = XmlWriter.Create(SavedTimePath)) new XmlSerializer(value.GetType()).Serialize(wrt, value);
}
}
internal string SavedTimePath
{
get
{
var p0 = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
var p1 = "Data.xml";
return System.IO.Path.Combine(p0, p1);
}
}
private DateTime LoadNextTime()
{
return DateTime.UtcNow;
}
#region IDisposable
private bool _disposed; // To detect redundant calls
public void Dispose()
{
Dispose(true); // Dispose of unmanaged resources.
GC.SuppressFinalize(this); // Suppress finalization.
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return; // redundant call
t?.Dispose(); // stop timer
_disposed = true; // protect against redundant calls
}
~TimeModel() => Dispose(false); // in case if Dispose was not called
#endregion
}
public class TimeData
{
public DateTime NextTime { get; set; }
}
}