✅ How do you handle countdowns of objects?
I have a List of Objects, each has an expiry date. I would like to display a countdown (e.g. 1 hour and 1 minute until expiry) in one of my views (WPF).
I'm unsure however, how to construct this in regards to timers. I calculate the time left by subtracting the current date from the date of expiration. If I do this in the object class and implement OnPropertyChanged I get a stackoverflow error, which is understandable because the subtraction would be calculated infinitely often. I also don't want to create a timer for every object because there could be quite a lot of those.
What is a better approach for this? I thought of creating a second collection that stores all the calculated values and is updated by a single timer, but I'm unsure how to implement this. I'd be grateful for your assistance!
24 Replies
how many items you can show on screen ? Expiration - CurrentDate should be correct approach
lets say 1 million items are in the list but only 100 of them shown in screen, so you get expiration date of those 100 items and start 100 instance of timer
Create a single timer which ticks one a second?
but you also need to extract elapsed time from the expiration of each item
In WPF you have the DispatcherTimer, you could use that: https://learn.microsoft.com/en-us/dotnet/api/system.windows.threading.dispatchertimer?view=windowsdesktop-8.0
DispatcherTimer Class (System.Windows.Threading)
A timer that is integrated into the Dispatcher queue which is processed at a specified interval of time and at a specified priority.
My problem with the approach of having a separate collection that is refreshed via a timer is that I’m not sure how to reference it in my View and bind to it, because it’s a different datacontext than the viewmodel I’m using. I can’t wrap my head around how to structure this in a meaningful way, how to keep track of each objects expiration. I’m a bit surprised there’s no easy example because I thought this was something relatively commonly implemented.
I'd do:
* ExpirableObjectVM has a property which returns time to go
* Parent VM has a collection of ExpirableObjectVM
* ParentView shows that collection
* ParentVM has a timer which fires once a whatever. When it fires, it goes through all the ExpirableObjectVMs in the collection and tells each to raise a PropertyChanged event for its "time to go" property
So you’d have two views (one used as a component) and two corresponding nested view models? Why not just use one view model? I’m, sorry but I’m a bit confused by having a collection of viewmodels. Could you explain this please? I’m always eager to learn if there’s a better way!
I'd probably only have 1 view. Yes it's common for a parent VM to have a collection of child VMs - that's how things like ItemsControl / ListView / ListBox work
I'm assuming you want to show the countdown for each of the objects in your list? In which case an ItemsControl is the normal way of displaying a list of children
I guess I deviated from the norm a little then. I’m using a single viewmodel and view, where I have a listbox with data template that binds to a collection in the viewmodel. And yes, I would like to show the countdown in the list. Do you have an idea where I could get an example for the timer per object implementation? It would really help to see how others have gone about doing this.
If you're binding to it, it's a ViewModel? That sounds like the structure I'm proposing
Well yes, but I only have one viewmodel that is in my views datacontext to which I can bind. You're suggesting to use two view models, making me unsure if I can get away with using one?
I'm not sure where this confusion is coming from I'm afraid.
If you have:
ParentVM.cs
ChildVM.cs
ParentView.xaml
Then you have one view (ParentView) and two ViewModels (ParentVM and ChildVM), and you bind to both (ParentView.xaml binds directly to ParentVM, and there's a DataTemplate in ParentView whose DataContext is set to each ChildVM and which binds to properties on ChildVM)
Thanks for expanding on this. I'm confused because I can't quite identify why using two ViewModels is necessary or solved the problem at hand (periodically updating collection of objects in an items control). I have a very similar setup, the difference being that I don't have a child VM but only a ViewModel for my View.
Thus I'm still stuck on implementing a solution using a single timer instead of one for each object in the list. I'm also curious to understand whether using two VMs is beneficial / the "correct" way? Thank you!
In the meantime, I thought I could maybe use OnPropertyChange in the get method of a property of minutes, but that seems to be impractical and not work at all.
Literally, how else would you structure this?
I have:
TestView.xaml
TestViewViewModel.cs
(simplified)
I have seen multiple VMs (your example) used in ItemControls before, but I didn't see any advantage in it and just continued doing it like I was used to.
So CarModel is a ViewModel. You're binding to its properties from a view. Why all the confusion?
You're right, my CarModel is your ChildViewModel. I was thinking there was a difference with some deeper meaning between Model and ViewModel?
A ViewModel is something you bind to, which implements INotifyPropertyChanged
So, next step. I'd do some variant on:
Thank you very much, this is the smartest solution i did not think of! I will get this working.
I mean, you can give each
CarViewModel
a timer if you want, but then you need to manage starting/stopping them, they might end up ticking at different times so your UI looks weird, etc
But, MVVM is straightforward. You want to show the time remaining? Put a property on the VM for "time remaining". You want that to update? Add a method to update that property. You want all children of a parent to update at the same time? Make the parent update all of its children at the same timeThis basically decribes your example from above, right? I just finished implementing it this way in my project and it works like a charm! The best thing is, it still feels MVVM conform and I can definitely see myself using something similar in many future situations. MVVM is straightforward...to those who already excel at it it seems 😉 I've been learning the pattern for this past year and I still find myself trapped in simple problems like these. Thanks again for taking the time to help me out, very much appreciated!
No worries, glad you got it working!
One last thing: if your
CarModel
is used outside of the UI layer, then don't go modifying it to add a TimeRemaining
property or add INPC
etc. It's fine to create a CarViewModel
which wraps your CarModel
, then all your MVVM stuff can go on CarViewModel
, and CarModel
can stay nice and pristine
$closeIf you have no further questions, please use /close to mark the forum thread as answered
Thanks for the tip, I will implement it this way once I progress further!