diff --git a/Wino.Mail.ViewModels/Data/MailItemViewModel.cs b/Wino.Mail.ViewModels/Data/MailItemViewModel.cs
index bcae3d2d..851d6f79 100644
--- a/Wino.Mail.ViewModels/Data/MailItemViewModel.cs
+++ b/Wino.Mail.ViewModels/Data/MailItemViewModel.cs
@@ -42,6 +42,15 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableRecipient,
[NotifyPropertyChangedRecipients]
public partial bool IsSelected { get; set; }
+ ///
+ /// Direct callback invoked when changes.
+ /// Used by the ListViewItem container to update its IsCustomSelected DP
+ /// without subscribing to INotifyPropertyChanged (faster, AOT-safe).
+ ///
+ public Action OnSelectionChanged { get; set; }
+
+ partial void OnIsSelectedChanged(bool value) => OnSelectionChanged?.Invoke(value);
+
///
/// Indicates if this mail item is currently being processed by a network operation.
/// Used to show loading state in the UI.
diff --git a/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs b/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs
index 5f360ee1..e8f9c5f6 100644
--- a/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs
+++ b/Wino.Mail.ViewModels/Data/ThreadMailItemViewModel.cs
@@ -14,6 +14,8 @@ namespace Wino.Mail.ViewModels.Data;
public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListItem
{
private readonly string _threadId;
+ private readonly HashSet _uniqueIdSet = [];
+ private MailItemViewModel _cachedLatestMailViewModel;
[ObservableProperty]
[NotifyPropertyChangedRecipients]
@@ -25,6 +27,15 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
public partial bool IsSelected { get; set; }
+ ///
+ /// Direct callback invoked when changes.
+ /// Used by the ListViewItem container to update its IsCustomSelected DP
+ /// without subscribing to INotifyPropertyChanged (faster, AOT-safe).
+ ///
+ public Action OnSelectionChanged { get; set; }
+
+ partial void OnIsSelectedChanged(bool value) => OnSelectionChanged?.Invoke(value);
+
[ObservableProperty]
public partial bool IsBusy { get; set; }
@@ -168,7 +179,7 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
[NotifyPropertyChangedFor(nameof(Base64ContactPicture))]
public partial ObservableCollection ThreadEmails { get; set; } = [];
- private MailItemViewModel latestMailViewModel => ThreadEmails.OrderByDescending(e => e.MailCopy?.CreationDate).FirstOrDefault()!;
+ private MailItemViewModel latestMailViewModel => _cachedLatestMailViewModel;
public DateTime SortingDate => CreationDate;
@@ -200,6 +211,8 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
}
ThreadEmails.Insert(insertIndex, email);
+ _uniqueIdSet.Add(email.MailCopy.UniqueId);
+ _cachedLatestMailViewModel = ThreadEmails[0];
// Reassign to trigger property change notifications
ThreadEmails = ThreadEmails;
}
@@ -211,6 +224,8 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
{
if (ThreadEmails.Remove(email))
{
+ _uniqueIdSet.Remove(email.MailCopy.UniqueId);
+ _cachedLatestMailViewModel = ThreadEmails.Count > 0 ? ThreadEmails[0] : null;
// Reassign to trigger property change notifications
ThreadEmails = ThreadEmails;
}
@@ -253,7 +268,7 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IMailListIte
///
/// Checks if this thread contains an email with the specified unique ID
///
- public bool HasUniqueId(Guid uniqueId) => ThreadEmails.Any(email => email.MailCopy.UniqueId == uniqueId);
+ public bool HasUniqueId(Guid uniqueId) => _uniqueIdSet.Contains(uniqueId);
public IEnumerable GetContainingIds() => ThreadEmails.Select(a => a.MailCopy.UniqueId);
diff --git a/Wino.Mail.WinUI/Controls/ListView/WinoListView.cs b/Wino.Mail.WinUI/Controls/ListView/WinoListView.cs
index 49fe2998..2ed82ab2 100644
--- a/Wino.Mail.WinUI/Controls/ListView/WinoListView.cs
+++ b/Wino.Mail.WinUI/Controls/ListView/WinoListView.cs
@@ -69,14 +69,12 @@ public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
&& container.Item != mailItemViewModel)
{
container.Item = mailItemViewModel;
- container.IsCustomSelected = mailItemViewModel.IsSelected;
}
else if (item is ThreadMailItemViewModel threadMailItemViewModel
&& element is WinoThreadMailItemViewModelListViewItem threadContainer
&& threadContainer.Item != threadMailItemViewModel)
{
threadContainer.Item = threadMailItemViewModel;
- threadContainer.IsSelected = threadMailItemViewModel.IsSelected;
threadContainer.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
}
}
@@ -88,12 +86,10 @@ public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
if (item is MailItemViewModel mailItemViewModel && element is WinoMailItemViewModelListViewItem container)
{
container.Item = null;
- container.IsCustomSelected = false;
}
else if (item is ThreadMailItemViewModel threadMailItemViewModel && element is WinoThreadMailItemViewModelListViewItem threadContainer)
{
threadContainer.Item = null;
- threadContainer.IsSelected = false;
threadContainer.IsThreadExpanded = false;
}
}
diff --git a/Wino.Mail.WinUI/Controls/ListView/WinoMailItemViewModelListViewItem.cs b/Wino.Mail.WinUI/Controls/ListView/WinoMailItemViewModelListViewItem.cs
index 81c8eea7..91f6816f 100644
--- a/Wino.Mail.WinUI/Controls/ListView/WinoMailItemViewModelListViewItem.cs
+++ b/Wino.Mail.WinUI/Controls/ListView/WinoMailItemViewModelListViewItem.cs
@@ -1,4 +1,3 @@
-using System.ComponentModel;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -23,26 +22,17 @@ public partial class WinoMailItemViewModelListViewItem : ListViewItem
partial void OnItemPropertyChanged(DependencyPropertyChangedEventArgs e)
{
- // TODO: This slows down. Optimize later.
- if (e.OldValue is MailItemViewModel oldMailItemViewModel) UnregisterPropertyChanged(oldMailItemViewModel);
- if (e.NewValue is MailItemViewModel newMailItemViewModel) RegisterPropertyChanged(newMailItemViewModel);
+ if (e.OldValue is MailItemViewModel oldItem)
+ oldItem.OnSelectionChanged = null;
- if (e.NewValue is MailItemViewModel mailItemViewModel)
- IsCustomSelected = mailItemViewModel.IsSelected;
- else
- IsCustomSelected = false;
- }
-
- private void RegisterPropertyChanged(MailItemViewModel model) => model.PropertyChanged += ModelPropertyChanged;
- private void UnregisterPropertyChanged(MailItemViewModel model) => model.PropertyChanged -= ModelPropertyChanged;
-
- private void ModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
- {
- if (sender is not MailItemViewModel mailItemViewModel) return;
-
- if (e.PropertyName == nameof(MailItemViewModel.IsSelected))
+ if (e.NewValue is MailItemViewModel newItem)
{
- IsCustomSelected = mailItemViewModel.IsSelected;
+ newItem.OnSelectionChanged = (selected) => IsCustomSelected = selected;
+ IsCustomSelected = newItem.IsSelected;
+ }
+ else
+ {
+ IsCustomSelected = false;
}
}
}
diff --git a/Wino.Mail.WinUI/Controls/ListView/WinoThreadMailItemViewModelListViewItem.cs b/Wino.Mail.WinUI/Controls/ListView/WinoThreadMailItemViewModelListViewItem.cs
index df9a719c..7114eec7 100644
--- a/Wino.Mail.WinUI/Controls/ListView/WinoThreadMailItemViewModelListViewItem.cs
+++ b/Wino.Mail.WinUI/Controls/ListView/WinoThreadMailItemViewModelListViewItem.cs
@@ -1,4 +1,3 @@
-using System.ComponentModel;
using System.Linq;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
@@ -40,20 +39,17 @@ public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
partial void OnItemPropertyChanged(DependencyPropertyChangedEventArgs e)
{
- if (e.OldValue is ThreadMailItemViewModel oldMailItemViewModel) UnregisterPropertyChanged(oldMailItemViewModel);
- if (e.NewValue is ThreadMailItemViewModel newMailItemViewModel) RegisterPropertyChanged(newMailItemViewModel);
- }
+ if (e.OldValue is ThreadMailItemViewModel oldItem)
+ oldItem.OnSelectionChanged = null;
- private void RegisterPropertyChanged(ThreadMailItemViewModel model) => model.PropertyChanged += ModelPropertyChanged;
- private void UnregisterPropertyChanged(ThreadMailItemViewModel model) => model.PropertyChanged -= ModelPropertyChanged;
-
- private void ModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
- {
- if (sender is not ThreadMailItemViewModel mailItemViewModel) return;
-
- if (e.PropertyName == nameof(ThreadMailItemViewModel.IsSelected))
+ if (e.NewValue is ThreadMailItemViewModel newItem)
{
- IsCustomSelected = mailItemViewModel.IsSelected;
+ newItem.OnSelectionChanged = (selected) => IsCustomSelected = selected;
+ IsCustomSelected = newItem.IsSelected;
+ }
+ else
+ {
+ IsCustomSelected = false;
}
}
}