尝试“脉冲” WPF 应用程序中的日期时间更新

发布于 2024-12-06 18:16:18 字数 1578 浏览 0 评论 0 原文

对于冗长的解释,我预先表示歉意。

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

在此处输入图像描述

基本上可以有 0..n 个进程列表中的状态项(项本身是使用 DataTemplate 呈现的 ListBoxItems)。最近,我决定提供“时间流逝”样式的演示(n 秒/分钟/小时前)作为绝对日期时间格式(mm/dd/yyyy at HH:mm:ss)的替代方案。我通过使用 MultiBinding 到 TextBlock 来完成此操作:

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

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

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

我尝试按照 这篇文章,但无济于事。

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

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

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

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

I apologize up front for the lengthy explication.

I am working on a WPF desktop app (shown below):

enter image description here

Basically there can be 0..n process status items in the list (the items themselves are ListBoxItems rendered using a DataTemplate). Recently, I decided I wanted to provide a "time elapsed" style presentation (n seconds/minutes/hours ago) as an alternative to an absolute DateTime format (mm/dd/yyyy at HH:mm:ss). I've accomplished this by using a MultiBinding to a TextBlock as such:

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

I'll spare you the details of the IMultiValueConverter implementation. The second binding is to a helper class that gets the user's timestamp display preferences (i.e. they can still see absolute DateTime values if they want to). The "StatusDateTime" property to which this TextBlock is otherwise bound is nothing special; it's just a POCO property in a ViewModel class that fires OnPropertyChanged when set.

This view is synchronized with the system backend every (x) seconds. I'd like to "Pulse" the timestamps during synchronization so that the elapsed time measurements stay accurate but if I just set the StatusDateTime property to its existing value, nothing seems to happen (in the case of the example below, it will just say "Published 6 seconds ago..." until either the process itself is updated on the server side or until the application is loaded a second time).

I tried adding IsAsync="true" to the StatusDateTime binding as suggested in this post, but to no avail.

I also tried falling back to regular binding (non-multi) with Mode="TwoWay" explicity declared and that didn't work either.

I know I could just blow away the list and recreate during synchronization, but that just doesn't seem very elegant when I already have all the data I need loaded into the client.

Update: I tried recreating this basic setup in a Window and it seemed to work just fine. I'm now wondering if this has something to do with the target control being on a Page or part of a DataTemplate.

Any thoughts? Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

抚你发端 2024-12-13 18:16:19

我在自己的基于 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 。

The way I accomplished this in my own MVVM based app was to have a simple timer running on my ViewModel that fires OnPropertyChanged every second for the property I want to keep updated. Here's an example (in IronPython syntax):

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")

This uses a System.Timers.Timer that executes its callback on a background ThreadPool thread, so it won't interfere with the Dispatcher.

遮云壑 2024-12-13 18:16:19

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

感谢所有回复的人!

Ok, so I believe I've figured it out. I left out a key piece of detail that more quickly would've pointed us in the right direction: My DataTemplate is backed by a CollectionViewSource. If I call the Refresh() method on the CollectionViewSource's View property, the timestamps update as expected. I always forget that changing properties of elements of an ObservableCollection doesn't necessarily cause the CollectionChanged event to fire which would thereby prompt the CollectionViewSource to refresh.

Thanks to all who replied!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文