【手把手实战教学】基于C#和.NET Framework的WinForms开发教程系列(3)自动定时更新

前言

开发环境:
IDE:Visual Studio 2026
语言:C#
框架:.NET Framework 4.5
UI:WinForms

本系列上一篇文章我们实现了开机自启功能,让程序可以在系统启动时自动运行。然而,很多工具类软件还需要在特定时间自动执行任务(比如每天下午6点同步数据)。本篇将教你如何实现定时自动更新功能,让程序在用户设定的时间自动完成数据同步。

一、为什么需要定时更新?

对于数据同步类应用,如果每次都要用户手动点击“同步”,不仅繁琐,还容易忘记。实现定时更新后,程序可以在用户指定的时间(如下班后)自动完成数据同步,既保证了数据的时效性,又无需用户干预。

二、实现原理

Windows Forms 提供了 System.Windows.Forms.Timer 控件,它会在 UI 线程上周期性触发 Tick 事件。我们可以利用它实现定时任务:

  1. 用户通过复选框 chkAutoSync 开启自动同步功能,并通过 DateTimePicker 控件 dtpAutoSyncTime 选择同步时间(如 18:00)。
  2. 程序启动时,读取用户上次保存的设置,并根据当前时间计算距离下一个同步时刻还有多少毫秒,将定时器的 Interval 设置为该值。
  3. 当定时器 Tick 事件触发时,执行数据同步(静默模式,不弹出消息框),然后重新计算下一次执行时间(即明天同一时刻)。
  4. 用户改变勾选状态或修改时间时,立即重新计算定时器间隔,并保存设置。

三、实现步骤

1. 设计界面

在窗体上添加两个控件:

  • CheckBox:命名为 chkAutoSync,Text 设为“自动同步”。
  • DateTimePicker:命名为 dtpAutoSyncTime,用于选择时间。

为了让 DateTimePicker 只显示小时和分钟(不显示日期),需要进行以下设置:

  • 在属性窗口中将 Format 属性改为 Custom
  • CustomFormat 属性设置为 "HH:mm"(注意大小写,HH 表示24小时制,mm 表示分钟)。
  • 显示时间调整按钮,将 ShowUpDown 设为 True

在这里插入图片描述

界面效果如图所示:
在这里插入图片描述

2. 添加字段与定时器

MainForm 类中添加以下字段:

// 自动同步定时器
private System.Windows.Forms.Timer autoSyncTimer;
// 防止自动同步重入
private bool isAutoSyncRunning = false;
// 防止在设置保存时重复触发事件
private bool isUpdatingAutoSyncSettings = false;

注意:由于项目中可能同时引用了 System.Threading.Timer 或其他命名空间,建议使用完全限定名 System.Windows.Forms.Timer 以避免歧义。

3. 在构造函数中初始化定时器并加载设置

public MainForm()
{
    InitializeComponent();
    // ... 其他初始化(如 syncService、DPI 适配等)...

    // 初始化自动同步定时器
    autoSyncTimer = new System.Windows.Forms.Timer();
    autoSyncTimer.Tick += AutoSyncTimer_Tick;

    // 加载保存的设置
    chkAutoSync.Checked = Properties.Settings.Default.AutoSyncEnabled;
    dtpAutoSyncTime.Value = DateTime.Parse(Properties.Settings.Default.AutoSyncTime);

    // 如果自动同步已开启,启动定时器
    if (chkAutoSync.Checked)
    {
        SetAutoSyncTimer();
    }
}

4. 实现计算下次执行时间的方法

/// <summary>
/// 根据用户设定的时间设置定时器间隔并启动
/// </summary>
private void SetAutoSyncTimer()
{
    // 如果未勾选自动同步,则停止定时器
    if (!chkAutoSync.Checked)
    {
        autoSyncTimer.Stop();
        return;
    }

    DateTime now = DateTime.Now;
    // 构造今天的同步目标时间(只取用户设定的小时和分钟)
    DateTime target = new DateTime(now.Year, now.Month, now.Day,
                                   dtpAutoSyncTime.Value.Hour,
                                   dtpAutoSyncTime.Value.Minute, 0);
    // 如果今天的目标时间已经过了,则设为明天
    if (target <= now)
    {
        target = target.AddDays(1);
    }

    // 计算相差的毫秒数,并设置定时器间隔
    double intervalMs = (target - now).TotalMilliseconds;
    autoSyncTimer.Interval = (int)intervalMs;
    autoSyncTimer.Start();
}

5. 实现定时器的 Tick 事件(自动同步执行)

private async void AutoSyncTimer_Tick(object sender, EventArgs e)
{
    // 先停止定时器,避免在执行过程中重复触发
    autoSyncTimer.Stop();

    if (isAutoSyncRunning) return;
    isAutoSyncRunning = true;

    try
    {
        // 调用同步服务,静默模式(不弹消息框)
        await syncService.SyncData(silent: true);
    }
    finally
    {
        isAutoSyncRunning = false;
        // 同步完成后,重新计算下一次执行时间(明天同一时刻)
        SetAutoSyncTimer();
    }
}

6. 处理用户交互事件

chkAutoSync 添加 CheckedChanged 事件:

private void chkAutoSync_CheckedChanged(object sender, EventArgs e)
{
    // 防止在保存设置时重复触发
    if (isUpdatingAutoSyncSettings) return;

    try
    {
        isUpdatingAutoSyncSettings = true;
        bool enabled = chkAutoSync.Checked;

        if (enabled)
            SetAutoSyncTimer();
        else
            autoSyncTimer.Stop();

        // 保存设置
        Properties.Settings.Default.AutoSyncEnabled = enabled;
        Properties.Settings.Default.Save();
    }
    finally
    {
        isUpdatingAutoSyncSettings = false;
    }
}

在控件chkAutoSync的事件设置CheckedChanged的值为chkAutoSync_CheckedChanged
在这里插入图片描述

dtpAutoSyncTime 添加 ValueChanged 事件:

private void dtpAutoSyncTime_ValueChanged(object sender, EventArgs e)
{
    if (isUpdatingAutoSyncSettings) return;

    try
    {
        isUpdatingAutoSyncSettings = true;
        // 保存用户选择的时间
        Properties.Settings.Default.AutoSyncTime = dtpAutoSyncTime.Value.ToString();
        Properties.Settings.Default.Save();

        // 如果自动同步已开启,重新计算定时器(使新的时间生效)
        if (chkAutoSync.Checked)
            SetAutoSyncTimer();
    }
    finally
    {
        isUpdatingAutoSyncSettings = false;
    }
}

在控件dtpAutoSyncTime的事件设置ValueChanged的值为dtpAutoSyncTime_ValueChanged
在这里插入图片描述

8. 添加应用程序设置

在项目属性 → 设置中,添加两个用户设置:

名称类型范围默认值
AutoSyncEnabledbool用户False
AutoSyncTimestring用户18:00:00

这样,用户的选择会被保存,下次启动程序时自动恢复。
在这里插入图片描述

四、测试验证

  1. 勾选自动同步:勾选 chkAutoSync,观察定时器是否按当前时间计算并启动(可在代码中输出 Interval 验证)。
  2. 修改同步时间:将时间改为稍后的几分钟(如当前时间+2分钟),等待定时器触发,观察程序是否自动执行同步(注意同步服务内部会有日志输出,可以设置断点或查看输出窗口)。
  3. 重启程序:关闭程序后重新运行,查看 chkAutoSync 是否保持勾选状态,且定时器按预期启动。
  4. 跨天测试:如果设定的时间已经过了当天,定时器应被设置为明天同一时间。

五、注意事项

  • 定时器的精度System.Windows.Forms.Timer 的精度约为 10-20 毫秒,对于小时级别的任务完全足够。
  • UI 线程阻塞:如果 SyncData 方法执行时间较长(如网络请求、文件操作),可能会阻塞 UI 线程,导致界面无响应。建议在服务内部使用异步方法,我们已经将 SyncData 设计为 async Task,不会阻塞 UI。
  • 系统时间更改:如果用户在程序运行期间修改了系统时间,定时器可能不会按预期执行。但正常使用场景下极少发生,可忽略。
  • 静默同步:自动同步时我们使用了 silent: true,服务内部不会弹出任何消息框,但会通过事件报告进度和错误。如有需要,可以在 UI 层记录日志或显示通知(如托盘气泡)。
  • 定时器的停止与重启:我们在 Tick 事件开头先停止定时器,执行完同步后再重新启动,这样可以避免在同步过程中再次触发(同步可能耗时)。同步完成后重新计算间隔,确保下一个周期正确。

六、总结

通过本文,我们成功实现了定时自动更新功能,用户只需勾选自动同步并设定时间,程序就会每天在指定时间静默执行数据同步。结合上一篇的开机自启,你的程序已经具备了后台运行的核心能力。

在下一篇中,我们将继续完善程序的用户体验,实现后台运行(系统托盘),让程序在最小化时隐藏到托盘区域,进一步提升易用性。敬请期待!


后记

文中所有代码均已验证,可直接复制到你的项目中,如有任何疑问,欢迎在评论区留言交流。


喜欢的点个关注吧><!祝你永无bug~

/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            佛祖保佑       永无BUG
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荔枝吻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值