绑定到与时间相关的属性

时间:2011-05-25 11:49:37

标签: wpf data-binding time dependencies notifications

前段时间我写了一个类似小部件的应用程序,它应该跟踪任务,每个任务都有一个指定为DateTime的截止日期,现在如果你想显示剩下多少时间,直到截止日期你可能想要绑定到“虚拟”( *诅咒virtual关键字 *)属性,如下所示:

public TimeSpan TimeLeft
{
    get { return Deadline - DateTime.Now; }
}

显然,从理论上讲,此属性会更改每个刻度,并且您希望不时更新您的UI(例如,通过定期为该属性发送PropertyChanged事件)。

当我写小部件时,我每分钟刷新整个任务列表,但这并不理想,因为如果用户与某个项目交互(例如通过键入绑定到Comments - 属性的TextBox)这将是严厉的中断,对源的更新会丢失。

那么,如果你有像这样的时间依赖属性,那么更新UI的最佳方法是什么呢?

我不再使用该应用程序,只是认为这是一个非常有趣的问题

3 个答案:

答案 0 :(得分:4)

计时器是我能想到的唯一方式。由于这是一个有趣的问题,我将把.02放进去。我会把它封装成这样的东西:

public class CountdownViewModel : INotifyPropertyChanged
{
    Func<TimeSpan> calc;
    DispatcherTimer timer;

    public CountdownViewModel(DateTime deadline)
        : this(() => deadline - DateTime.Now)
    {
    }

    public CountdownViewModel(Func<TimeSpan> calculator)
    {
        calc = calculator;

        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Tick += timer_Tick;
        timer.Start();
    }

    void timer_Tick(object sender, EventArgs e)
    {
        var temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs("CurrentValue"));
        }
    }

    public TimeSpan CurrentValue
    {
        get
        {
            var result = calc();
            if (result < TimeSpan.Zero)
            {
                return TimeSpan.Zero;
            }
            return result;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class MyViewModel
{
    public CountdownViewModel DeadlineCountdown { get; private set; }

    public DateTime Deadline { get; set; }

    public MyViewModel()
    {
        Deadline = DateTime.Now.AddSeconds(200);
        DeadlineCountdown = new CountdownViewModel(Deadline);
    }
}

然后您可以直接绑定到DeadlineCountdown.CurrentValue,或创建CountdownView。如果需要,您可以将计时器移动到CountdownView。您可以使用静态计时器,以便它们同时更新。

修改

如果Deadline将要改变,你必须像这样构建倒计时:

DeadlineCountdown = new CountdownViewModel(() => this.Deadline - DateTime.Now);

答案 1 :(得分:3)

我认为您在代码示例后的第一段中所说的是在WPF中使其工作的唯一合理方法。设置一个只为PropertyChanged属性调用TimeLeft的计时器。间隔会根据您的情况而有所不同(如果您正在谈论每周任务列表,您可能只需要在5分钟左右更新它。如果您在接下来的30分钟内谈论任务列表,您可能需要每分钟或每30秒更新一次。

该方法可以避免您在刷新选项中提到的问题,因为只有TimeLeft绑定会受到影响。如果你有数百万的这些任务,我想性能损失将是非常重要的。但是,如果你只有几十个或者其他东西,那么每30秒左右更新这些绑定将是一个相当微不足道的问题,对吗?

我能想到的每种可能性都使用定时器或动画。将任务添加到列表时,动画会太“沉重”。在Timer方案中,上面的方案似乎是最干净,最简单和最实用的方案。可能只是归结为它是否适用于您的特定场景。

答案 2 :(得分:1)

我读了你接受的答案,但我只是想知道......为什么不在“编辑”模式下禁用该特定任务的绑定,这样你就不会被打断?然后只需在完成后重新启用该绑定,或者取消编辑?那样即使你的计时器每秒更新一次,谁在乎呢?

至于如何在不分离它们的情况下禁用它们(从而重置它们的值),只需定义一个布尔标志,然后在所有要中断的DP中,检查验证逻辑中的那个标志。如果该标志为true且它所应用的DependencyObject是您正在编辑的那个,则阻止对DP的更改。

无论如何,这只是突然出现在我脑海中。实际上没有对它进行测试,但尝试它应该是一件容易的事。