New WinoListView implementation with multiple selections.

This commit is contained in:
Burak Kaan Köse
2025-10-26 14:53:22 +01:00
parent d4c8ae6cb7
commit 79d5b6ed40
22 changed files with 748 additions and 480 deletions
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Windows.Input;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml.Controls;
@@ -6,6 +7,7 @@ using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.WinUI.Controls.Advanced;
[Obsolete("ItemsView sucks. Hard to deal with virtualization issues. Use ListView. This control is here to wise up anyone who tries to use it.")]
public partial class WinoItemsView : ItemsView
{
private const string PART_ScrollView = nameof(PART_ScrollView);
@@ -0,0 +1,49 @@
using Microsoft.UI.Xaml;
using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.WinUI.Controls.ListView;
public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
{
public bool IsAllSelected => Items.Count == SelectedItems.Count;
protected override DependencyObject GetContainerForItemOverride() => new WinoListViewItem();
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
{
WinoListViewItem? itemContainer = null;
foreach (var item in Items)
{
if (item is MailItemViewModel mailItem && mailItem.Id == mailItemViewModel.Id)
{
itemContainer = ContainerFromItem(mailItemViewModel) as WinoListViewItem;
break;
}
else if (item is ThreadMailItemViewModel threadMailItemViewModel && threadMailItemViewModel.HasUniqueId(mailItemViewModel.MailCopy.UniqueId))
{
itemContainer = ContainerFromItem(threadMailItemViewModel) as WinoListViewItem;
// Try to get the inner WinoListView.
if (itemContainer != null)
{
itemContainer.IsExpanded = true;
var innerListViewControl = itemContainer.GetWinoListViewControl();
if (innerListViewControl != null)
{
itemContainer = innerListViewControl.ContainerFromItem(mailItemViewModel) as WinoListViewItem;
}
}
break;
}
}
itemContainer?.IsSelected = true;
return itemContainer != null;
}
}
@@ -0,0 +1,103 @@
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Wino.Mail.ViewModels.Data;
using Wino.Messaging.Client.Mails;
namespace Wino.Mail.WinUI.Controls.ListView;
public partial class WinoListViewItem : ListViewItem
{
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(nameof(IsExpanded), typeof(bool), typeof(WinoListViewItem), new PropertyMetadata(false, OnIsExpandedChanged));
public WinoListViewItem()
{
DefaultStyleKey = typeof(WinoListViewItem);
RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
}
private static void OnIsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is WinoListViewItem item)
{
// Handle expansion state change if needed
}
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
if (oldContent is IMailListItem oldMailItem)
{
UnregisterSelectionCallback(oldMailItem);
}
if (newContent is IMailListItem newMailItem)
{
IsSelected = newMailItem.IsSelected;
RegisterSelectionCallback(newMailItem);
}
}
private void UnregisterSelectionCallback(IMailListItem mailItem)
{
mailItem.PropertyChanged -= MailPropChanged;
}
private void RegisterSelectionCallback(IMailListItem mailItem)
{
mailItem.PropertyChanged += MailPropChanged;
}
// From model
private void MailPropChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (sender is not IMailListItem mailItem) return;
if (e.PropertyName == nameof(IMailListItem.IsSelected)) ApplySelectionForContainer(mailItem);
}
// From container.
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
{
if (Content is IMailListItem mailItem)
{
ApplySelectionForModel(mailItem);
}
}
private void ApplySelectionForModel(IMailListItem mailItem)
{
if (mailItem.IsSelected != IsSelected)
{
mailItem.IsSelected = IsSelected;
WeakReferenceMessenger.Default.Send(new SelectedItemsChangedMessage());
}
}
private void ApplySelectionForContainer(IMailListItem mailItem)
{
if (IsSelected != mailItem.IsSelected)
{
IsSelected = mailItem.IsSelected;
}
}
public WinoListView? GetWinoListViewControl()
{
var expander = GetTemplateChild("ExpanderPart") as Expander;
if (expander?.Content is ContentPresenter presenter) return VisualTreeHelper.GetChild(presenter, 0) as WinoListView;
return null;
}
}
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Wino.Mail.WinUI.Controls.ListView">
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary>
<!-- Thread Mail ListViewItem Style -->
<Style x:Key="DefaultThreadListViewItemStyle" TargetType="local:WinoListViewItem">
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Background" Value="{ThemeResource ListViewItemBackground}" />
<Setter Property="Foreground" Value="{ThemeResource ListViewItemForeground}" />
<Setter Property="TabNavigation" Value="Local" />
<Setter Property="IsHoldingEnabled" Value="True" />
<Setter Property="Padding" Value="16,0,12,0" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="MinWidth" Value="{ThemeResource ListViewItemMinWidth}" />
<Setter Property="MinHeight" Value="{ThemeResource ListViewItemMinHeight}" />
<Setter Property="AllowDrop" Value="False" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="1" />
<Setter Property="FocusVisualPrimaryBrush" Value="{ThemeResource ListViewItemFocusVisualPrimaryBrush}" />
<Setter Property="FocusVisualPrimaryThickness" Value="2" />
<Setter Property="FocusVisualSecondaryBrush" Value="{ThemeResource ListViewItemFocusVisualSecondaryBrush}" />
<Setter Property="FocusVisualSecondaryThickness" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:WinoListViewItem">
<Expander Header="Thread" IsExpanded="{TemplateBinding IsExpanded}">
<Expander.Content>
<!-- Expandable Content -->
<ContentPresenter
x:Name="ThreadContent"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Default Single Mail List View Item Style. -->
<Style
x:Key="DefaultMailListViewItemStyle"
BasedOn="{StaticResource DefaultListViewItemStyle}"
TargetType="local:WinoListViewItem" />
<local:WinoMailItemContainerStyleSelector
x:Name="WinoMailItemContainerStyleSelector"
MailItemStyle="{StaticResource DefaultMailListViewItemStyle}"
ThreadStyle="{StaticResource DefaultThreadListViewItemStyle}" />
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
@@ -0,0 +1,19 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.WinUI.Controls.ListView;
public partial class WinoMailItemContainerStyleSelector : StyleSelector
{
public Style? ThreadStyle { get; set; }
public Style? MailItemStyle { get; set; }
protected override Style SelectStyleCore(object item, DependencyObject container)
{
if (item is MailItemViewModel) return MailItemStyle ?? throw new Exception($"Missing style for {nameof(MailItemViewModel)}");
if (item is ThreadMailItemViewModel) return ThreadStyle ?? throw new Exception($"Missing style for {nameof(ThreadMailItemViewModel)}");
return null;
}
}
@@ -0,0 +1,22 @@
using System;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Wino.Mail.ViewModels.Data;
namespace Wino.Mail.WinUI.Controls.ListView;
public partial class WinoMailItemTemplateSelector : DataTemplateSelector
{
public DataTemplate? SingleMailItemTemplate { get; set; }
public DataTemplate? ThreadMailItemTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if (item is MailItemViewModel)
return SingleMailItemTemplate ?? throw new Exception($"Missing template for single mail items.");
else if (item is ThreadMailItemViewModel)
return ThreadMailItemTemplate ?? throw new Exception($"Missing template for thread mail items.");
return base.SelectTemplateCore(item, container);
}
}