Files
Tornado3_2026Election/Tornado3_2026Election/Controls/ChannelSchedulePanel.xaml.cs

162 lines
4.6 KiB
C#

using System;
using System.Threading.Tasks;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using Windows.Foundation;
using Tornado3_2026Election.Domain;
using Tornado3_2026Election.ViewModels;
namespace Tornado3_2026Election.Controls;
public sealed partial class ChannelSchedulePanel : UserControl
{
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(
nameof(ViewModel),
typeof(ChannelScheduleViewModel),
typeof(ChannelSchedulePanel),
new PropertyMetadata(null));
public ChannelSchedulePanel()
{
InitializeComponent();
}
public ChannelScheduleViewModel? ViewModel
{
get => (ChannelScheduleViewModel?)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
private void PromoteToNextButton_Click(object sender, RoutedEventArgs e)
{
var item = GetItem(sender);
var command = ViewModel?.PromoteToNextCommand;
if (item is null || command is null || !command.CanExecute(item))
{
return;
}
command.Execute(item);
}
private void MoveUpButton_Click(object sender, RoutedEventArgs e)
{
_ = AnimateSwapAsync(sender, moveUp: true);
}
private void MoveDownButton_Click(object sender, RoutedEventArgs e)
{
_ = AnimateSwapAsync(sender, moveUp: false);
}
private void RemoveItemButton_Click(object sender, RoutedEventArgs e)
{
var item = GetItem(sender);
var command = ViewModel?.RemoveItemCommand;
if (item is null || command is null || !command.CanExecute(item))
{
return;
}
command.Execute(item);
}
private static ChannelScheduleItem? GetItem(object sender)
{
return (sender as FrameworkElement)?.DataContext as ChannelScheduleItem;
}
private async Task AnimateSwapAsync(object sender, bool moveUp)
{
if (ViewModel is null)
{
return;
}
var item = GetItem(sender);
if (item is null)
{
return;
}
var currentIndex = ViewModel.Queue.IndexOf(item);
var targetIndex = currentIndex + (moveUp ? -1 : 1);
if (currentIndex < 0 || targetIndex < 0 || targetIndex >= ViewModel.Queue.Count)
{
return;
}
var swappedItem = ViewModel.Queue[targetIndex];
var movingContainer = QueueListView.ContainerFromItem(item) as ListViewItem;
var swappedContainer = QueueListView.ContainerFromItem(swappedItem) as ListViewItem;
var movingTop = movingContainer is null ? (double?)null : GetTop(movingContainer);
var swappedTop = swappedContainer is null ? (double?)null : GetTop(swappedContainer);
var command = moveUp ? ViewModel.MoveUpCommand : ViewModel.MoveDownCommand;
if (!command.CanExecute(item))
{
return;
}
command.Execute(item);
await Task.Yield();
QueueListView.UpdateLayout();
if (movingTop.HasValue)
{
AnimateToCurrentPosition(QueueListView.ContainerFromItem(item) as ListViewItem, movingTop.Value);
}
if (swappedTop.HasValue)
{
AnimateToCurrentPosition(QueueListView.ContainerFromItem(swappedItem) as ListViewItem, swappedTop.Value);
}
}
private double GetTop(FrameworkElement element)
{
return element.TransformToVisual(QueueListView).TransformPoint(new Point(0, 0)).Y;
}
private void AnimateToCurrentPosition(ListViewItem? container, double previousTop)
{
if (container is null)
{
return;
}
var currentTop = GetTop(container);
var delta = previousTop - currentTop;
if (Math.Abs(delta) < 0.5)
{
return;
}
var transform = container.RenderTransform as TranslateTransform;
if (transform is null)
{
transform = new TranslateTransform();
container.RenderTransform = transform;
}
transform.Y = delta;
var animation = new DoubleAnimation
{
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(180)),
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
var storyboard = new Storyboard();
storyboard.Children.Add(animation);
Storyboard.SetTarget(animation, transform);
Storyboard.SetTargetProperty(animation, nameof(TranslateTransform.Y));
storyboard.Begin();
}
}