【问题标题】:Trying to "Pulse" DateTime Updates in WPF App尝试在 WPF 应用程序中“脉冲”日期时间更新
【发布时间】:2011-09-26 20:06:40
【问题描述】:

对于冗长的解释,我深表歉意。

我正在开发 WPF 桌面应用程序(如下所示):

列表中基本上可以有 0..n 个进程状态项(这些项本身是使用 DataTemplate 呈现的 ListBoxItems)。最近,我决定要提供“经过时间”样式的演示文稿(n 秒/分钟/小时前),以替代绝对日期时间格式(mm/dd/yyyy at HH:mm:ss)。我已经通过对 TextBlock 使用 MultiBinding 来实现这一点:

<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter = "{StaticResource timeElapsedConverter}">
            <Binding 
                Path   = "StatusDateTime"
            />
            <Binding 
                Source = "{StaticResource propertiesHelper}" 
                Path   = "UserOptions.UseElapsedTimeStamps" 
            />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

我不会详细介绍 IMultiValueConverter 实现的细节。第二个绑定是到一个帮助类,它获取用户的时间戳显示首选项(即,如果他们愿意,他们仍然可以看到绝对的 DateTime 值)。此 TextBlock 绑定到的“StatusDateTime”属性没有什么特别之处;它只是 ViewModel 类中的一个 POCO 属性,在设置时会触发 OnPropertyChanged。

此视图每 (x) 秒与系统后端同步一次。我想在同步期间“脉冲”时间戳,以便经过时间测量保持准确,但如果我只是将 StatusDateTime 属性设置为其现有值,似乎什么都没有发生(在下面的示例中,它只会说“Published 6 seconds ago...”,直到进程本身在服务器端更新或应用程序再次加载)。

我尝试按照this post 中的建议将 IsAsync="true" 添加到 StatusDateTime 绑定,但无济于事。

我还尝试使用 Mode="TwoWay" 显式声明的常规绑定(非多重),但这也不起作用。

我知道我可以在同步期间删除列表并重新创建,但是当我已经将所有需要的数据加载到客户端时,这似乎不太优雅。

更新:我尝试在窗口中重新创建这个基本设置,它似乎工作得很好。我现在想知道这是否与目标控件位于页面或 DataTemplate 的一部分有关。

有什么想法吗?提前致谢!

【问题讨论】:

  • 你说你解雇了 OnPropertyChanged。验证您正在触发 NotifyPropertyChanged("StatusDateTime");不能想当然。
  • 所以你将 DateTime 变量与 DateTime.Now() 进行比较并显示结果 TimeSpan?
  • @BahriGungor - 我验证了 StatusDateTime 字段的 PropertyChanged 事件正在触发。
  • @Baboon - 不,我正在绑定到我的 ViewModel 类中的 StatusDateTime 属性,并且希望所述绑定会在基础属性值更新时导致 UI 更新。

标签: c# .net wpf xaml mvvm


【解决方案1】:

我在自己的基于 MVVM 的应用程序中实现这一点的方法是在我的 ViewModel 上运行一个简单的计时器,该计时器每秒为我想要保持更新的属性触发 OnPropertyChanged。这是一个示例(使用 IronPython 语法):

self.displayTimer = System.Timers.Timer(1000) #fire every second
self.displayTimer.Elapsed += self.displayTimer_Tick
self.displayTimer.Start()

def displayTimer_Tick(self, sender, event):
    self.OnPropertyChanged("StatusDateTime")

这使用 System.Timers.Timer 在后台 ThreadPool 线程上执行其回调,因此它不会干扰 Dispatcher。

【讨论】:

  • 这是一种有趣的方法,但我仍然希望有一种纯粹的 MVVM 方法可以让它发挥作用。
  • 这个 MVVM。 ;) 您明确避免在视图中引用控件;相反,您从 ViewModel 发出 INotifyPropertyChanged 通知,通知绑定到该属性的所有绑定以获取新值。
  • 我同意 Aphex。 MVVM 更多的是关于依赖的分离,而不是关于线程。但是,您可能希望在更高级别旋转线程以发送所有正在运行的进程的通知,而不是为 N 个进程旋转 N 个线程。
  • 点了。我实际上正在做类似@BahriGungor 建议的事情(一个覆盖所有项目的更高级别的同步线程)。但问题不在于实现的选择,而在于它实际上并没有像看起来应该的那样更新 UI 以响应 PropertyChanged 事件。
【解决方案2】:

好的,所以我相信我已经弄清楚了。我遗漏了一个可以更快地为我们指明正确方向的关键细节:我的DataTemplate 得到CollectionViewSource 的支持。如果我在CollectionViewSourceView 属性上调用 Refresh() 方法,时间戳会按预期更新。我总是忘记更改ObservableCollection 元素的属性并不一定会导致CollectionChanged 事件触发,从而促使CollectionViewSource 刷新。

感谢所有回复的人!

【讨论】:

  • 这在功能上等同于在每个元素上调用OnPropertyChanged :) 不过干得好。实际上,您可能对 BindingList&lt;T&gt; 而不是 ObservableCollection 感兴趣 - 它是一个特殊的集合,它实际上从其项目中侦听 INotifyPropertyChanged 通知,这意味着更改单个元素的属性会正确地传播更改,就像您期望的那样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
  • 2013-01-28
  • 2015-04-18
  • 1970-01-01
  • 2011-05-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多