前段时间我写了一个类似小部件的应用程序,它应该跟踪任务,每个任务都有一个指定为DateTime
的截止日期,现在如果你想显示剩下多少时间,直到截止日期你可能想要绑定到“虚拟”( *诅咒virtual
关键字 *)属性,如下所示:
public TimeSpan TimeLeft
{
get { return Deadline - DateTime.Now; }
}
显然,从理论上讲,此属性会更改每个刻度,并且您希望不时更新您的UI(例如,通过定期为该属性发送PropertyChanged
事件)。
当我写小部件时,我每分钟刷新整个任务列表,但这并不理想,因为如果用户与某个项目交互(例如通过键入绑定到Comments
- 属性的TextBox)这将是严厉的中断,对源的更新会丢失。
那么,如果你有像这样的时间依赖属性,那么更新UI的最佳方法是什么呢?
(我不再使用该应用程序,只是认为这是一个非常有趣的问题)
答案 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的更改。
无论如何,这只是突然出现在我脑海中。实际上没有对它进行测试,但尝试它应该是一件容易的事。