You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于TimeSpan变量计算到当前时间的已流逝时长

定时器持久化实现方案

核心思路

每个ListView条目绑定独立的持久化标识和计时数据,避免多条目定时器互相干扰,沿用你现有DateTime计算耗时的逻辑即可实现需求。

实现步骤

  1. 定义条目持久化字段
    每个条目需要独立存储3个核心数据:
  • 条目唯一ID(用于区分不同条目的持久化键)
  • 条目启动时间戳(DateTime类型,持久化存储)
  • 条目总时长(就是你现有TimeSheet.TotalTime的整数值,和条目绑定存储)
  1. 单条目启动计时逻辑
// 假设当前点击的待启动条目为selectedItem,包含Id、TotalTime属性
// 构造唯一持久化键,避免和其他条目冲突
var startAtKey = $"Timer_Start_{selectedItem.Id}";
var startAt = DateTime.Now;
// 持久化当前条目的启动时间
Application.Current.Properties[startAtKey] = startAt;
// 你原有的总时长转换逻辑保留
var totalDuration = TimeSpan.FromMinutes(selectedItem.TotalTime);
// 绑定取消令牌到当前条目,方便后续手动停止
var cts = new CancellationTokenSource();
selectedItem.TimerCts = cts;

Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
    if (cts.IsCancellationRequested)
    {
        return false;
    }
    // 计算已流逝时长,和你参考示例逻辑完全一致
    var elapsedTime = DateTime.Now - startAt;
    // 可选:判断是否达到总时长,自动停止
    if (elapsedTime >= totalDuration)
    {
        cts.Cancel();
        // 持久化删除已完成的计时数据
        Application.Current.Properties.Remove(startAtKey);
        Device.BeginInvokeOnMainThread(() =>
        {
            selectedItem.TxtTimer = "计时完成";
        });
        return false;
    }
    // 更新当前条目的计时显示
    Device.BeginInvokeOnMainThread(() =>
    {
        selectedItem.TxtTimer = elapsedTime.ToString(@"hh\:mm\:ss");
    });
    return true;
});
  1. 应用重启后恢复计时逻辑
    如果需要实现杀应用后重启仍能接续计时,在页面初始化时遍历所有状态为「计时中」的条目,读取持久化的启动时间重新启动定时器即可:
// 遍历所有计时中的条目
foreach (var runningItem in ListViewDataSource.Where(x => x.IsTiming))
{
    var startAtKey = $"Timer_Start_{runningItem.Id}";
    if (Application.Current.Properties.TryGetValue(startAtKey, out var savedStartObj) 
        && savedStartObj is DateTime savedStartAt)
    {
        var totalDuration = TimeSpan.FromMinutes(runningItem.TotalTime);
        var cts = new CancellationTokenSource();
        runningItem.TimerCts = cts;
        // 重启定时器,流逝时长直接用当前时间减历史启动时间,自动补全中间离线的时长
        Device.StartTimer(TimeSpan.FromSeconds(1), () =>
        {
            if (cts.IsCancellationRequested) return false;
            var elapsedTime = DateTime.Now - savedStartAt;
            if (elapsedTime >= totalDuration)
            {
                cts.Cancel();
                Application.Current.Properties.Remove(startAtKey);
                Device.BeginInvokeOnMainThread(() => runningItem.TxtTimer = "计时完成");
                return false;
            }
            Device.BeginInvokeOnMainThread(() => runningItem.TxtTimer = elapsedTime.ToString(@"hh\:mm\:ss"));
            return true;
        });
    }
}
  1. 手动停止计时处理
    用户手动停止某条目标计时时,执行以下逻辑即可:
// 假设当前要停止的条目为stopItem
stopItem.TimerCts?.Cancel();
// 删除持久化的启动时间
Application.Current.Properties.Remove($"Timer_Start_{stopItem.Id}");
stopItem.TxtTimer = "00:00:00";

注意:如果遇到Application.Current.Properties存储DateTime出现序列化异常,可以把启动时间转成Unix时间戳(long类型)存储,读取时再转回DateTime,兼容性更好。

内容的提问来源于stack exchange,提问作者PHULUSO GOVERN RAMULIFHO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 04:42:01