using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Threading; using System.Timers; using System.Windows; /* 功能:定时启动一个操作。 作者:李茂平 用法: this.TimeWork = new ClassLibraryEF.TimeWork(this.MainWindow); this.TimeWork.AddWork(new ClassLibraryEF.TimeWorkItem() { 任务名称 = "任务名称1", Work = this.DoWork1, 计划启动日期 = "周六 5月1日 9月1日 11月1日", 计划启动时间 = "08:00", 任务说明 = "任务说明" }); this.TimeWork.AddWork(new ClassLibraryEF.TimeWorkItem() { 任务名称 = "任务名称2", Work = this.DoWork2, 计划启动时间 = "09:30 10:30 11:31 14:00 15:01", 任务说明 = "任务说明" }); this.TimeWork.SetEnable(true); */ namespace ClassLibraryEF { /// <summary> /// 定时执行任务的类,精度每分钟 /// </summary> public class TimeWork : ObservableCollection<TimeWorkItem> { /// <summary> /// 使用多线程计时器 /// </summary> private readonly System.Timers.Timer timer1 = new System.Timers.Timer(); /// <summary> /// 运行的分钟数 /// </summary> private int CountMin { get; set; } = 0; /// <summary> /// 当任务完成时 /// </summary> public Action<TimeWorkItem, TimeWorkLogItem> OnWorkFinished { get; set; } /// <summary> /// 执行记录 /// </summary> public ObservableCollection<TimeWorkLogItem> LogItems { get; set; } = new ObservableCollection<TimeWorkLogItem>(); public Window MainWindow { get; } /// <summary> /// 构造函数,创建精度为1分钟的定时器 /// </summary> public TimeWork(Window mainwindow) { this.MainWindow = mainwindow; this.timer1.Interval = 1 * 1000 * 60; // 1分钟的定时器 this.timer1.Elapsed += timerMinute_Elapsed; this.timer1.Enabled = false; // 初始化为不发生定时事件 } /// <summary> /// 是否启用定时任务 /// </summary> /// <param name="bEnable"></param> public void SetEnable(bool bEnable) { this.timer1.Enabled = bEnable; if (bEnable) ResetRunTime(); } /// <summary> /// 定时器时间到,检查启动项目 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public void timerMinute_Elapsed(object sender, ElapsedEventArgs e) { CountMin++; if (CountMin % 60 == 0) // 每1个小时重置一次 { ResetRunTime(); } DoTimerWork(); } /// <summary> /// 重新设定运行时间 /// </summary> public void ResetRunTime() { foreach (var item in this) { item.InitRunTimes(); } } /// <summary> /// 定时器时间到,检查启动项目 /// </summary> private void DoTimerWork() { DateTime now = DateTime.Now; foreach (var item in this) { foreach (var time in item.RunTimes) { if (time.Hours == now.Hour && time.Minutes == now.Minute) { Thread t = new Thread(() => { this.DoWork(item); }); t.Start(); } } } } /// <summary> /// 添加一个定时执行的项目 /// </summary> /// <param name="item"></param> public void AddWork(TimeWorkItem item) { this.Add(item); } /// <summary> /// 执行任务 /// </summary> /// <param name="item"></param> public void DoWork(TimeWorkItem item) { if (item != null && item.Work != null) { DateTime begin = DateTime.Now; item.Work(); // 执行工作 item.上次执行时间 = begin.ToString("T"); if (item.添加到执行记录) LogItem(item, begin, DateTime.Now); } } /// <summary> /// 添加一条记录到文件,记录已经执行的任务 /// </summary> /// <param name="item"></param> private void LogItem(TimeWorkItem item, DateTime begin, DateTime end) { TimeWorkLogItem logItem = new TimeWorkLogItem(); logItem.任务名称 = item.任务名称; logItem.执行时间 = begin; logItem.完成时间 = end; this.MainWindow.Dispatcher.Invoke(() => { this.LogItems.Add(logItem); if (OnWorkFinished != null) { OnWorkFinished(item, logItem); } }); } } /// <summary> /// MVVM模式下,更改属性的通知 /// 首先定义NotificationObject类。目的是绑定数据属性。 /// 这个类的作用是实现了INotifyPropertyChanged接口。 /// WPF中类要实现这个接口,其属性成员才具备通知UI的能力 /// </summary> public class NotificationObjectEF : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } /// <summary> /// 定时工作项目 /// </summary> public class TimeWorkItem : NotificationObjectEF { public Action Work { get; set; } public string 任务名称 { get; set; } public string 任务说明 { get; set; } /// <summary> /// 设定的计划启动日期,空格分开,如:每周6,每月1,空白意味着每天 /// 空白 -- 启动 /// 周一 到 周日 启动 /// 5月2日 启动 /// </summary> public string 计划启动日期 { get; set; } = "每天"; /// <summary> /// 空格分开的多个时间:15:00 16:32 /// </summary> public string 计划启动时间 { get; set; } /// <summary> /// 间隔多少分钟重复,每天执行 /// </summary> public int 间隔分钟 { get; set; } /// <summary> /// 是否记录到执行记录 /// </summary> public bool 添加到执行记录 { get; set; } = true; private string 上次执行时间_; public string 上次执行时间 { get { return 上次执行时间_; } set { 上次执行时间_ = value; RaisePropertyChanged("上次执行时间"); } } /// <summary> /// 运行任务的时间表 /// </summary> public ObservableCollection<TimeSpan> RunTimes { get; set; } = new ObservableCollection<TimeSpan>(); /// <summary> /// 运行任务的时间表 /// </summary> private string runTimeStr; /// <summary> /// 运行任务的时间表 /// </summary> public string RunTimesStr { get { return runTimeStr; } set { runTimeStr = value; RaisePropertyChanged("RunTimesStr"); } } /// <summary> /// 初始化时间 /// </summary> public void InitRunTimes() { // 分析时间 RunTimes.Clear(); if (!string.IsNullOrEmpty(计划启动时间) && IsTodayRun()) { string[] times = 计划启动时间.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var item in times) { DateTime dt = new DateTime(); if (DateTime.TryParse(item, out dt)) { RunTimes.Add(dt.TimeOfDay); } } } if (间隔分钟 > 0 && IsTodayRun() && string.IsNullOrEmpty(计划启动时间)) { DateTime now = DateTime.Now; DateTime oneTime = now.AddMinutes(间隔分钟); DateTime EndTime = now.AddHours(12); while (oneTime <= EndTime) { RunTimes.Add(oneTime.TimeOfDay); oneTime = oneTime.AddMinutes(间隔分钟); } } // 设置字符串 string newRunTimesStr = ""; foreach (TimeSpan ts in RunTimes) { newRunTimesStr += $"{ts.Hours:00}:{ts.Minutes:00} "; } RunTimesStr = newRunTimesStr; } /// <summary> /// 判断今日是否是执行日期. /// 空白 -- 是 /// 周一到周日 是 /// 5月2日 是 /// </summary> /// <returns></returns> private bool IsTodayRun() { if (string.IsNullOrEmpty(this.计划启动日期)) return true; if (this.计划启动日期.Contains("每天")) return true; string[] days = 计划启动日期.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (var item in days) { if (item.Contains("周") || item.Contains("星期")) { if (DateTime.Now.DayOfWeek == ConvertToDayOfWeek(item)) return true; } if (item.Contains("月") && item.Contains("日")) { string full = $"{DateTime.Now.Year}年" + item; if (DateTime.TryParse(full, out DateTime setDate)) { if (setDate.Date == DateTime.Now.Date) return true; } } } return false; } private DayOfWeek ConvertToDayOfWeek(string item) { switch (item) { case "周一": case "星期一": return DayOfWeek.Monday; case "周二": case "星期二": return DayOfWeek.Tuesday; case "周三": case "星期三": return DayOfWeek.Wednesday; case "周四": case "星期四": return DayOfWeek.Thursday; case "周五": case "星期五": return DayOfWeek.Monday; case "周六": case "星期六": return DayOfWeek.Saturday; case "周日": case "星期日": return DayOfWeek.Sunday; default: return DayOfWeek.Sunday; } } } [Serializable] public class TimeWorkLogItem { /// <summary> /// 名称 /// </summary> public string 任务名称 { get; set; } /// <summary> /// 上次执行时间 /// </summary> public DateTime 执行时间 { get; set; } /// <summary> /// 完成时间 /// </summary> public DateTime 完成时间 { get; set; } } }