T
Honestly, I'm not sure about this, just my outline.I wanted to make sure that every element in the collection was one independent object that had its time of life. Of course it's MVM.Well, first of all, we need an object. What's in it?Name.Time of life.End time.The rest of the time.Timer "life."Timer tic.I ended up doing this class:public class TimerViewModel : VM
{
public TimerViewModel(string name, TimeSpan time)
{
Name = name;
Time = time;
End = DateTime.Now.Add(Time);
StartTimer();
}
public string Name { get; set; }
public TimeSpan Time { get; set; }
public DateTime End { get; set; }
private TimeSpan _timeLeft;
public TimeSpan TimeLeft
{
get => _timeLeft;
set
{
_timeLeft = value;
OnPropertyChanged();
}
}
public event Action<TimerViewModel> Ended;
private DispatcherTimer _deleteTimer;
private DispatcherTimer _updateTimer;
private void StartTimer()
{
_deleteTimer = new DispatcherTimer();
_updateTimer = new DispatcherTimer();
_deleteTimer.Interval = End - DateTime.Now;
_updateTimer.Interval = TimeSpan.FromSeconds(1);
_deleteTimer.Tick += DeleteTimerOnTick;
_updateTimer.Tick += UpdateTimerOnTick;
_deleteTimer.Start();
_updateTimer.Start();
}
private void UpdateTimerOnTick(object sender, EventArgs e)
{
TimeLeft = End - DateTime.Now;
}
private void DeleteTimerOnTick(object sender, EventArgs e)
{
_deleteTimer.Stop();
_updateTimer.Stop();
Ended?.Invoke(this);
}
}
This class inherits standard INotifyPropertyChanged (to capture changes to UI). The essence is quite simple: Create the necessary properties and event that will serve us to alert the main VM to the need to remove the element. Through the designer, apply the necessary properties (for convenience), calculate the completion time, and launch the timers. In the timer launch method, we initiate two timers (one ticking once a second, another equal to the object ' s life), we sign them them to our processors (because a second ticking is just updating the properties with the rest of the time, and the main is stopping the timer and warning the event). Let's start all this miracle.Okay, we have a VM facility now. The main VM, complete it and make View.Major ViewModel, what does it require? Collect with our timers.When the element is added/designed, we have to sign for its disposal event.Method of removal.Let's try:public class MainViewModel : VM
{
public MainViewModel()
{
Timers = new ObservableCollection<TimerViewModel>();
Timers.CollectionChanged += TimersOnCollectionChanged;
Timers.Add(new TimerViewModel("Таймер 1", TimeSpan.FromSeconds(5)));
Timers.Add(new TimerViewModel("Таймер 2", TimeSpan.FromSeconds(10)));
Timers.Add(new TimerViewModel("Таймер 3", TimeSpan.FromSeconds(15)));
Timers.Add(new TimerViewModel("Таймер 4", TimeSpan.FromSeconds(20)));
Timers.Add(new TimerViewModel("Таймер 5", TimeSpan.FromSeconds(25)));
Timers.Add(new TimerViewModel("Таймер 6", TimeSpan.FromSeconds(30)));
Timers.Add(new TimerViewModel("Таймер 7", TimeSpan.FromSeconds(35)));
Timers.Add(new TimerViewModel("Таймер 8", TimeSpan.FromSeconds(35)));
Timers.Add(new TimerViewModel("Таймер 9", TimeSpan.FromMinutes(30)));
}
public ObservableCollection<TimerViewModel> Timers { get; set; }
private void TimersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (TimerViewModel newItem in e.NewItems)
{
newItem.Ended += DeleteOldItemsEvent;
}
}
if (e.OldItems != null)
{
foreach (TimerViewModel newItem in e.OldItems)
{
newItem.Ended -= DeleteOldItemsEvent;
}
}
}
private void DeleteOldItemsEvent(TimerViewModel obj) => Timers.Remove(obj);
}
What's going on here?Create our ObservableCollection.In the designer, we initialize the collection, we complete it, and we sign up for the CollectionChanged event (to know the addition/outcome of its facilities).We're working on the CollectionChanged event. In it, we'll check if there's an old/new object/objects, sign or write off from the disposal event.We're doing a disposal technique, we're just removing the transferable event from the collection.Well, View stayed. For a species you want, you should use WrapPanel, and because we're dynamically building objects, we should use ItemsControl. I'm not gonna explain this much, XAML is basically like,<ItemsControl ItemsSource="{Binding Timers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border CornerRadius="5" BorderBrush="Gray" BorderThickness="1" Width="70" Height="70" Margin="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Margin="3">
<TextBlock Text="{Binding Name}" FontWeight="Medium"/>
<TextBlock Text="{Binding TimeLeft, StringFormat={0:hh\:mm\:ss}}"/>
</StackPanel>
<Border Grid.Row="1" Background="#FFDADADA" BorderBrush="Gray" BorderThickness="0,1,0,0">
<TextBlock Text="{Binding End, StringFormat={0:hh\:mm\:ss}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Well, we're getting DataContext and we're happy with the result: In fact... All of this can be replaced by one timer in MainViewModel, which will be ticked once a second and tied to the main time-frame of the current time, updating and others. I just showed one of the options.So good luck programming