Удаление кнопок по времени

202
21 июня 2018, 10:50

День добрый, был бы признателен, если бы подсказали как удалять кнопки и ставить на их место соседнюю по наступлению времени.

Исходное Допустим наступило 7 утра и вот такое желательно должно получиться

Answer 1

Честно, не уверен в данном примере, чисто мои наброски.
Хотел сделать так, что бы каждый элемент в коллекции был одним независимым объектом, который имеет свое время жизни. Естественно все по правилам MVVM.

И так, для начала нам нужен сам объект. Что в нем должно быть?

  1. Имя.
  2. Время жизни.
  3. Время окончания.
  4. Оставшееся время.
  5. Таймер "жизни".
  6. Таймер тика.

В итоге я сделал такой класс:

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);
    }
}

Данный класс наследует стандартный INotifyPropertyChanged (для отлавливание изменений в UI). Суть довольно проста:

  1. Создаем необходимые свойства и event, который послужит нам для оповещении главной VM об необходимости удалить элемент.
  2. Через конструктор задаем необходимые свойства (для удобства), подсчитываем время завершения, а также запускаем таймеры.
  3. В методе запуска таймеров инициализируем два таймера (один тикает раз в секунду, другой равен жизни объекта), подписываем их на свои обработчики (у того, что секунду тикает - просто обновляем свойство с оставшимся временем, а у основного - останавливаем таймер и оповещаем event). Ну и запускаем все это чудо.

Хорошо, у нас теперь есть VM объекта. Осталось сделать основную VM, заполнить ее и сделать View.

Основная ViewModel, что от нее требуется?

  1. Коллекция с нашими таймерами.
  2. При добавление/удаление элемента мы должны подписаться на его event удаления.
  3. Метод самого удаления.

Ну что, попробуем:

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);
}

Что тут происходит:

  1. Создаем свойство нашей ObservableCollection.
  2. В конструкторе инициализируем коллекцию, заполняем ее, а также подписываемся на событие CollectionChanged (что бы знать об добавление/удаление объектов в ней).
  3. Реализуем обработчик события CollectionChanged. В нем мы проверяем, если есть старый/новый объект/объекты, то подписываемся или отписываемся от события удаления.
  4. Реализуем метод удаления, тут просто удаляем из коллекции переданный event'ом объект.

Ну что, осталась View. Для такого вида, который хотите вы - стоит использовать WrapPanel, а из за того, что мы динамически создаем объекты - стоит использовать ItemsControl. Тут объяснять особо не буду, XAML в целом получился такой:

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

Ну что, задаем DataContext и радуемся результатом:

По сути... Все это можно заменить на один таймер в MainViewModel, который будет раз в секунду тикать и к нему уже привязать основной таймер текущего времени, обновление и др. Я лишь показал один из возможных вариантов.

В общем удачи в программирование ;-)

READ ALSO
парсинг простой структуры JSON [дубликат]

парсинг простой структуры JSON [дубликат]

На данный вопрос уже ответили:

137
MySQL запрос. Можно ли доработать?

MySQL запрос. Можно ли доработать?

Как добавить вывод имени, соответствующего максимальному значению?

171
New description field in OpenCart

New description field in OpenCart

Who can say me how to create same field like this in admin product pageAnd show in product card

164