using System;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.UI.Controls;
using StrixMusic.Sdk.WinUI.Controls.Items.Abstract;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
namespace StrixMusic.Sdk.WinUI.Controls.Collections.Abstract
{
///
/// A Templated Control base for showing items with progressive loading.
///
/// The data type.
/// The container type.
[TemplatePart(Name = nameof(PART_Selector), Type = typeof(Selector))]
public abstract partial class CollectionControl : Control
where TData : class
where TItem : ItemControl
{
///
/// The backing for the property.
///
public static readonly DependencyProperty EmptyContentProperty =
DependencyProperty.Register(
nameof(EmptyContent),
typeof(FrameworkElement),
typeof(CollectionControl),
new PropertyMetadata(null, (d, e) => ((CollectionControl)d).SetNoContentTemplate((FrameworkElement)e.NewValue)));
///
/// Fired when the selected item changes
///
public event EventHandler>? SelectionChanged;
///
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
Loaded += CollectionControl_Loaded;
}
///
/// The content to show when this is empty.
///
public FrameworkElement EmptyContent
{
get => (FrameworkElement)GetValue(EmptyContentProperty);
set => SetValue(EmptyContentProperty, value);
}
private Control? PART_Selector { get; set; }
private ScrollViewer? PART_Scroller { get; set; }
private ContentPresenter? PART_EmptyContentPresenter { get; set; }
///
/// Clears all selected items.
///
public void ClearSelected()
{
if (PART_Selector == null)
return;
if (PART_Selector is Selector selector)
selector.SelectedItem = null;
else if (PART_Selector is DataGrid dataGrid)
dataGrid.SelectedItem = null;
}
///
/// Perform incremental loading.
///
/// A representing the asynchronous operation.
protected abstract Task LoadMore();
///
/// Checks if the collection has no content.
///
protected abstract void CheckAndToggleEmpty();
///
/// Sets the visibility of the empty content.
///
protected void SetEmptyVisibility(Visibility visibility)
{
Guard.IsNotNull(PART_EmptyContentPresenter, nameof(PART_EmptyContentPresenter));
PART_EmptyContentPresenter.Visibility = visibility;
}
private void AttachHandlers()
{
Unloaded += CollectionControl_Unloaded;
if (PART_Selector == null || PART_Scroller == null)
return;
if (PART_Selector is Selector selector)
selector.SelectionChanged += SelectedItemChanged;
else if (PART_Selector is DataGrid dataGrid)
dataGrid.SelectionChanged += SelectedItemChanged;
else return;
PART_Scroller.ViewChanged += CollectionControl_ViewChanged;
}
private void DetachHandlers()
{
Loaded -= CollectionControl_Loaded;
if (PART_Selector == null || PART_Scroller == null)
return;
if (PART_Selector is Selector selector)
selector.SelectionChanged -= SelectedItemChanged;
else if (PART_Selector is DataGrid dataGrid)
dataGrid.SelectionChanged -= SelectedItemChanged;
else return;
PART_Scroller!.ViewChanged -= CollectionControl_ViewChanged;
}
private void CollectionControl_Loaded(object sender, RoutedEventArgs e)
{
// Find and set Selector
PART_Selector = VisualTreeHelpers.GetDataTemplateChild(this, nameof(PART_Selector));
if (PART_Selector == null)
{
PART_Selector = VisualTreeHelpers.GetDataTemplateChild(this, nameof(PART_Selector));
}
// Find and set Scroller
PART_Scroller = VisualTreeHelpers.GetDataTemplateChild(this);
AttachHandlers();
_ = LoadMore();
}
private void CollectionControl_Unloaded(object sender, RoutedEventArgs e)
{
DetachHandlers();
}
private void CollectionControl_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
CheckScrollPosition();
}
private void CheckScrollPosition()
{
if (PART_Scroller == null)
return;
double fromBottom = PART_Scroller.ScrollableHeight - PART_Scroller.VerticalOffset;
// If approaching the bottom of the list
if (fromBottom < 100)
{
_ = LoadMore();
}
}
private void SelectedItemChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems)
{
TItem? container = GetItemFromData(item);
if (container != null)
{
container.Selected = true;
}
}
foreach (var item in e.RemovedItems)
{
TItem? container = GetItemFromData(item);
if (container != null)
{
container.Selected = false;
}
}
if (PART_Selector == null)
return;
// Get selected item
// Invoke event
TData data;
if (PART_Selector is Selector selector)
data = (TData)selector.SelectedItem;
else if (PART_Selector is DataGrid dataGrid)
data = (TData)dataGrid.SelectedItem;
else
return;
Events.SelectionChangedEventArgs selectionChangedEventArgs = new Events.SelectionChangedEventArgs(data);
SelectionChanged?.Invoke(this, selectionChangedEventArgs);
}
private TItem? GetItemFromData(object data)
{
if (PART_Selector is Selector selector)
return VisualTreeHelpers.FindVisualChildren(selector.ContainerFromItem(data)).FirstOrDefault();
return null;
}
private void SetNoContentTemplate(FrameworkElement frameworkElement)
{
if (PART_EmptyContentPresenter != null)
{
PART_EmptyContentPresenter.Content = frameworkElement;
}
}
}
}