Files
Wino-Mail/Wino.Mail.WinUI/Controls/ListView/WinoThreadMailItemViewModelListViewItem.cs
T
2025-10-27 12:53:15 +01:00

131 lines
4.2 KiB
C#

using System.ComponentModel;
using System.Linq;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Controls;
using Wino.Helpers;
using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.WinUI.Controls.ListView;
public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
{
public bool IsThreadExpanded
{
get { return (bool)GetValue(IsThreadExpandedProperty); }
set { SetValue(IsThreadExpandedProperty, value); }
}
public static readonly DependencyProperty IsThreadExpandedProperty = DependencyProperty.Register(nameof(IsThreadExpanded), typeof(bool), typeof(WinoThreadMailItemViewModelListViewItem), new PropertyMetadata(false, new PropertyChangedCallback(OnIsThreadExpandedChanged)));
private readonly long _selectionChangeCallbackToken;
public WinoThreadMailItemViewModelListViewItem()
{
DefaultStyleKey = typeof(WinoThreadMailItemViewModelListViewItem);
_selectionChangeCallbackToken = RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
}
public void Cleanup()
{
if (Content is ThreadMailItemViewModel mailItem)
{
UnregisterSelectionCallback(mailItem);
UnregisterPropertyChangedCallback(IsSelectedProperty, _selectionChangeCallbackToken);
}
}
private static void OnIsThreadExpandedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
{
// 1. Reflect expansion changes to WinoExpander.
// 2. Automatically select first item on expansion, if none selected.
// 3. Unselect all items on collapse.
var control = sender as WinoThreadMailItemViewModelListViewItem;
var innerControl = control?.GetWinoListViewControl();
var expander = control?.GetExpander();
if (innerControl == null || control == null || expander == null) return;
// 2
if (control.IsThreadExpanded && innerControl.SelectedItems.Count == 0 && innerControl.Items.Count > 0)
{
innerControl.SelectedItems.Clear();
// Make item selected, container might not be realized yet, so set on the model.
// It'll appear selected when container is realized.
var firstItem = innerControl.Items.FirstOrDefault() as MailItemViewModel;
firstItem?.IsSelected = true;
}
// 1
expander.IsExpanded = control.IsThreadExpanded;
// 3
if (!control.IsSelected) innerControl?.SelectedItems.Clear();
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
if (oldContent is ThreadMailItemViewModel oldMailItem)
{
UnregisterSelectionCallback(oldMailItem);
}
if (newContent is ThreadMailItemViewModel newMailItem)
{
IsSelected = newMailItem.IsSelected;
RegisterSelectionCallback(newMailItem);
}
}
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
{
IsThreadExpanded = IsSelected;
}
public void UnregisterSelectionCallback(ThreadMailItemViewModel mailItem)
{
mailItem.PropertyChanged -= MailPropChanged;
}
private void MailPropChanged(object? sender, PropertyChangedEventArgs e)
{
if (sender is not ThreadMailItemViewModel mailItem) return;
if (e.PropertyName == nameof(ThreadMailItemViewModel.IsThreadExpanded))
{
ApplySelectionForContainer(mailItem);
}
}
private void RegisterSelectionCallback(ThreadMailItemViewModel mailItem)
{
mailItem.PropertyChanged -= MailPropChanged;
mailItem.PropertyChanged += MailPropChanged;
}
private void ApplySelectionForContainer(ThreadMailItemViewModel mailItem)
{
if (IsThreadExpanded != mailItem.IsThreadExpanded) IsThreadExpanded = mailItem.IsThreadExpanded;
}
public WinoListView? GetWinoListViewControl()
{
var expander = GetExpander();
if (expander?.Content is WinoListView control) return control;
return null;
}
public WinoExpander? GetExpander() => WinoVisualTreeHelper.FindDescendants<WinoExpander>(this).FirstOrDefault();
}