New list view items.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using Windows.System;
|
||||
using Microsoft.UI.Input;
|
||||
using Windows.System;
|
||||
using Windows.UI.Core;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
|
||||
namespace Wino.Core.WinUI.Services;
|
||||
@@ -8,8 +8,8 @@ namespace Wino.Core.WinUI.Services;
|
||||
public class KeyPressService : IKeyPressService
|
||||
{
|
||||
public bool IsCtrlKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
=> InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
|
||||
|
||||
public bool IsShiftKeyPressed()
|
||||
=> Window.Current?.CoreWindow?.GetKeyState(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down) ?? false;
|
||||
=> InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
|
||||
}
|
||||
|
||||
@@ -724,6 +724,27 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
await NotifySelectionChangesAsync();
|
||||
}
|
||||
|
||||
private IEnumerable<IMailListItem> AllItemsIncludingThreads
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var group in _mailItemSource)
|
||||
{
|
||||
foreach (var item in group)
|
||||
{
|
||||
if (item is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
foreach (var child in threadMailItemViewModel.ThreadEmails)
|
||||
{
|
||||
yield return child;
|
||||
}
|
||||
}
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<MailItemViewModel> AllItems
|
||||
{
|
||||
get
|
||||
@@ -752,7 +773,7 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
public bool IsAllItemsSelected => AllItems.Any() && AllItems.All(a => a.IsSelected);
|
||||
public bool HasSingleItemSelected => SelectedItemsCount == 1;
|
||||
|
||||
public async Task ExecuteWithoutRaiseSelectionChangedAsync(Action<MailItemViewModel> action)
|
||||
public async Task ExecuteWithoutRaiseSelectionChangedAsync(Action<IMailListItem> action, bool includeThreads)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -761,9 +782,19 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
foreach (var item in AllItems)
|
||||
if (includeThreads)
|
||||
{
|
||||
action(item);
|
||||
foreach (var item in AllItemsIncludingThreads)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in AllItems)
|
||||
{
|
||||
action(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -816,8 +847,9 @@ public class WinoMailCollection : ObservableRecipient, IRecipient<SelectedItemsC
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true);
|
||||
public Task UnselectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false);
|
||||
public Task SelectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = true, true);
|
||||
public Task UnselectAllAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => a.IsSelected = false, true);
|
||||
public Task CollapseAllThreadsAsync() => ExecuteWithoutRaiseSelectionChangedAsync(a => { if (a is ThreadMailItemViewModel thread) thread.IsThreadExpanded = false; }, true);
|
||||
|
||||
private async Task ExecuteUIThread(Action action) => await CoreDispatcher?.ExecuteOnUIThread(action);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Wino.Mail.ViewModels.Data;
|
||||
/// <summary>
|
||||
/// Single view model for IMailItem representation.
|
||||
/// </summary>
|
||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IMailListItem
|
||||
public partial class MailItemViewModel(MailCopy mailCopy) : ObservableRecipient, IMailListItem
|
||||
{
|
||||
public DateTime CreationDate => MailCopy.CreationDate;
|
||||
[ObservableProperty]
|
||||
@@ -18,6 +18,7 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IM
|
||||
public partial bool ThumbnailUpdatedEvent { get; set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
public partial bool IsSelected { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -89,10 +90,6 @@ public partial class MailItemViewModel(MailCopy mailCopy) : ObservableObject, IM
|
||||
set => SetProperty(MailCopy.HasAttachments, value, MailCopy, (u, n) => u.HasAttachments = n);
|
||||
}
|
||||
|
||||
partial void OnIsSelectedChanged(bool oldValue, bool newValue)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> GetContainingIds() => [MailCopy.UniqueId];
|
||||
|
||||
public IEnumerable<MailItemViewModel> GetSelectedMailItems()
|
||||
|
||||
@@ -17,11 +17,16 @@ public partial class ThreadMailItemViewModel : ObservableRecipient, IDisposable,
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
||||
public partial bool IsThreadExpanded { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedRecipients]
|
||||
[NotifyPropertyChangedFor(nameof(IsSelectedOrExpanded))]
|
||||
public partial bool IsSelected { get; set; }
|
||||
|
||||
public bool IsSelectedOrExpanded => IsSelected || IsThreadExpanded;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of emails in this thread
|
||||
/// </summary>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using MoreLinq;
|
||||
using Nito.AsyncEx;
|
||||
using Serilog;
|
||||
@@ -39,7 +40,8 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
||||
IRecipient<NewMailSynchronizationRequested>,
|
||||
IRecipient<AccountSynchronizerStateChanged>,
|
||||
IRecipient<AccountCacheResetMessage>,
|
||||
IRecipient<ThumbnailAdded>
|
||||
IRecipient<ThumbnailAdded>,
|
||||
IRecipient<PropertyChangedMessage<bool>>
|
||||
{
|
||||
private bool isChangingFolder = false;
|
||||
|
||||
@@ -221,7 +223,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
||||
var selectedItem = MailCollection.SelectedItems.ElementAtOrDefault(0);
|
||||
ActiveMailItemChanged(selectedItem);
|
||||
}
|
||||
else if (MailCollection.SelectedItemsCount > 1)
|
||||
else if (MailCollection.SelectedItemsCount == 0)
|
||||
{
|
||||
ActiveMailItemChanged(null);
|
||||
}
|
||||
@@ -311,20 +313,6 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
||||
{
|
||||
if (_activeMailItem == selectedMailItemViewModel) return;
|
||||
|
||||
// Don't update active mail item if Ctrl key is pressed or multi selection is enabled.
|
||||
// User is probably trying to select multiple items.
|
||||
// This is not the same behavior in Windows Mail,
|
||||
// but it's a trash behavior.
|
||||
|
||||
var isCtrlKeyPressed = _keyPressService.IsCtrlKeyPressed();
|
||||
|
||||
bool isMultiSelecting = isCtrlKeyPressed || IsMultiSelectionModeEnabled;
|
||||
|
||||
if (isMultiSelecting && StatePersistenceService.IsReaderNarrowed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activeMailItem = selectedMailItemViewModel;
|
||||
|
||||
Messenger.Send(new ActiveMailItemChangedEvent(_activeMailItem));
|
||||
@@ -1102,6 +1090,7 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
||||
Messenger.Register<AccountSynchronizerStateChanged>(this);
|
||||
Messenger.Register<AccountCacheResetMessage>(this);
|
||||
Messenger.Register<ThumbnailAdded>(this);
|
||||
Messenger.Register<PropertyChangedMessage<bool>>(this);
|
||||
}
|
||||
|
||||
protected override void UnregisterRecipients()
|
||||
@@ -1115,5 +1104,26 @@ public partial class MailListPageViewModel : MailBaseViewModel,
|
||||
Messenger.Unregister<AccountSynchronizerStateChanged>(this);
|
||||
Messenger.Unregister<AccountCacheResetMessage>(this);
|
||||
Messenger.Unregister<ThumbnailAdded>(this);
|
||||
Messenger.Unregister<PropertyChangedMessage<bool>>(this);
|
||||
}
|
||||
|
||||
public void Receive(PropertyChangedMessage<bool> message)
|
||||
{
|
||||
// Handle IsSelected property changes from MailItemViewModel
|
||||
if (message.PropertyName == nameof(MailItemViewModel.IsSelected) && message.Sender is MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
Messenger.Send(new SelectedItemsChangedMessage());
|
||||
}
|
||||
else if (message.Sender is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
if (message.PropertyName == nameof(ThreadMailItemViewModel.IsSelected))
|
||||
{
|
||||
// Thread selected.
|
||||
}
|
||||
else if (message.PropertyName == nameof(ThreadMailItemViewModel.IsThreadExpanded))
|
||||
{
|
||||
// Thread expanded.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Linq;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
|
||||
@@ -6,10 +7,20 @@ namespace Wino.Mail.WinUI.Controls.ListView;
|
||||
|
||||
public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
|
||||
{
|
||||
public bool IsThreadListView
|
||||
{
|
||||
get { return (bool)GetValue(IsThreadListViewProperty); }
|
||||
set { SetValue(IsThreadListViewProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsThreadListViewProperty = DependencyProperty.Register(nameof(IsThreadListView), typeof(bool), typeof(WinoListView), new PropertyMetadata(false));
|
||||
|
||||
public bool IsAllSelected => Items.Count == SelectedItems.Count;
|
||||
|
||||
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
|
||||
{
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
|
||||
if (item is MailItemViewModel mailItemViewModel && element is WinoMailItemViewModelListViewItem container)
|
||||
{
|
||||
// Ensure the container's selection state matches the model's state
|
||||
@@ -19,18 +30,52 @@ public partial class WinoListView : Microsoft.UI.Xaml.Controls.ListView
|
||||
}
|
||||
else if (item is ThreadMailItemViewModel threadMailItemViewModel && element is WinoThreadMailItemViewModelListViewItem threadContainer)
|
||||
{
|
||||
threadContainer.IsSelected = threadMailItemViewModel.IsSelected;
|
||||
threadContainer.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
|
||||
}
|
||||
|
||||
base.PrepareContainerForItemOverride(element, item);
|
||||
}
|
||||
|
||||
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
|
||||
public WinoMailItemViewModelListViewItem? GetMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
if (element is WinoThreadMailItemViewModelListViewItem threadMailItemViewModelListViewItem) threadMailItemViewModelListViewItem.Cleanup();
|
||||
if (element is WinoMailItemViewModelListViewItem winoMailItemViewModelListViewItem) winoMailItemViewModelListViewItem.Cleanup();
|
||||
foreach (var item in Items)
|
||||
{
|
||||
if (item is MailItemViewModel mailItem && mailItem.Id == mailItemViewModel.Id) return ContainerFromItem(mailItemViewModel) as WinoMailItemViewModelListViewItem;
|
||||
if (item is ThreadMailItemViewModel threadMailItem && threadMailItem.GetContainingIds().Contains(mailItemViewModel.MailCopy.UniqueId))
|
||||
{
|
||||
var threadContainer = ContainerFromItem(threadMailItem) as WinoThreadMailItemViewModelListViewItem;
|
||||
|
||||
base.ClearContainerForItemOverride(element, item);
|
||||
// Try to get the inner WinoListView.
|
||||
if (threadContainer != null)
|
||||
{
|
||||
var innerListViewControl = threadContainer.GetWinoListViewControl();
|
||||
|
||||
return innerListViewControl?.ContainerFromItem(mailItemViewModel) as WinoMailItemViewModelListViewItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public WinoThreadMailItemViewModelListViewItem? GetThreadMailItemContainer(ThreadMailItemViewModel threadMailItemViewModel)
|
||||
=> ContainerFromItem(threadMailItemViewModel) as WinoThreadMailItemViewModelListViewItem;
|
||||
|
||||
public void ToggleItemContainer(IMailListItem mailListItem)
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (mailListItem is MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
var container = GetMailItemContainer(mailItemViewModel);
|
||||
container?.IsSelected = mailItemViewModel.IsSelected;
|
||||
}
|
||||
else if (mailListItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
var container = GetThreadMailItemContainer(threadMailItemViewModel);
|
||||
container?.IsSelected = threadMailItemViewModel.IsSelected;
|
||||
container?.IsThreadExpanded = threadMailItemViewModel.IsThreadExpanded;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||
|
||||
@@ -11,37 +11,76 @@
|
||||
|
||||
<ResourceDictionary>
|
||||
<!-- Thread Mail ListViewItem Style -->
|
||||
<Style x:Key="DefaultThreadListViewItemStyle" TargetType="local:WinoMailItemViewModelListViewItem">
|
||||
<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" />
|
||||
<Style
|
||||
x:Key="DefaultThreadListViewItemStyle"
|
||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||
TargetType="local:WinoThreadMailItemViewModelListViewItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:WinoMailItemViewModelListViewItem">
|
||||
<!-- Expandable Content -->
|
||||
<ContentPresenter
|
||||
x:Name="ThreadContent"
|
||||
Margin="{TemplateBinding Padding}"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
<ControlTemplate TargetType="local:WinoThreadMailItemViewModelListViewItem">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Grid.ColumnSpan="2"
|
||||
Background="{ThemeResource ListViewItemBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}" />
|
||||
<Border
|
||||
x:Name="SelectionIndicator"
|
||||
Width="4"
|
||||
Margin="0,16"
|
||||
HorizontalAlignment="Center"
|
||||
Background="{ThemeResource ListViewItemSelectionIndicatorBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Item.IsSelected, Mode=OneWay}" />
|
||||
<!-- Expandable Content -->
|
||||
<ContentPresenter
|
||||
x:Name="ThreadContent"
|
||||
Grid.Column="1"
|
||||
Margin="6,0,0,0"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPointerOver}" />
|
||||
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelected}" />
|
||||
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SelectedPointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPointerOver}" />
|
||||
<Setter Target="ThreadContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
@@ -52,7 +91,76 @@
|
||||
<Style
|
||||
x:Key="DefaultMailListViewItemStyle"
|
||||
BasedOn="{StaticResource DefaultListViewItemStyle}"
|
||||
TargetType="local:WinoMailItemViewModelListViewItem" />
|
||||
TargetType="local:WinoMailItemViewModelListViewItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:WinoMailItemViewModelListViewItem">
|
||||
<Grid
|
||||
x:Name="RootGrid"
|
||||
Margin="0,2"
|
||||
Background="{ThemeResource ListViewItemBackground}"
|
||||
CornerRadius="{ThemeResource ControlCornerRadius}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
x:Name="SelectionIndicator"
|
||||
Width="4"
|
||||
Margin="0,16"
|
||||
HorizontalAlignment="Center"
|
||||
Background="{ThemeResource ListViewItemSelectionIndicatorBrush}"
|
||||
CornerRadius="4"
|
||||
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Item.IsSelected, Mode=OneWay}" />
|
||||
|
||||
<ContentPresenter
|
||||
x:Name="MailContent"
|
||||
Grid.Column="1"
|
||||
Margin="6,0,0,0"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="Transparent"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPointerOver}" />
|
||||
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundPointerOver}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelected}" />
|
||||
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="SelectedPointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPointerOver}" />
|
||||
<Setter Target="MailContent.Foreground" Value="{ThemeResource ListViewItemForegroundSelected}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PressedSelected">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="RootGrid.Background" Value="{ThemeResource ListViewItemBackgroundSelectedPressed}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<local:WinoMailItemContainerStyleSelector
|
||||
x:Name="WinoMailItemContainerStyleSelector"
|
||||
|
||||
@@ -12,7 +12,8 @@ public partial class WinoMailItemContainerStyleSelector : StyleSelector
|
||||
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)}");
|
||||
if (item is ThreadMailItemViewModel)
|
||||
return ThreadStyle ?? throw new Exception($"Missing style for {nameof(ThreadMailItemViewModel)}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,86 +1,23 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Messaging.Client.Mails;
|
||||
|
||||
namespace Wino.Mail.WinUI.Controls.ListView;
|
||||
|
||||
public partial class WinoMailItemViewModelListViewItem : ListViewItem
|
||||
{
|
||||
private readonly long _selectionChangeCallbackToken;
|
||||
[GeneratedDependencyProperty]
|
||||
public partial MailItemViewModel? Item { get; set; }
|
||||
|
||||
public WinoMailItemViewModelListViewItem()
|
||||
{
|
||||
DefaultStyleKey = typeof(WinoMailItemViewModelListViewItem);
|
||||
|
||||
_selectionChangeCallbackToken = RegisterPropertyChangedCallback(IsSelectedProperty, OnIsSelectedChanged);
|
||||
}
|
||||
|
||||
protected override void OnContentChanged(object oldContent, object newContent)
|
||||
{
|
||||
base.OnContentChanged(oldContent, newContent);
|
||||
|
||||
if (oldContent is MailItemViewModel oldMailItem)
|
||||
{
|
||||
UnregisterSelectionCallback(oldMailItem);
|
||||
}
|
||||
|
||||
if (newContent is MailItemViewModel newMailItem)
|
||||
{
|
||||
IsSelected = newMailItem.IsSelected;
|
||||
RegisterSelectionCallback(newMailItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
if (Content is MailItemViewModel mailItem)
|
||||
{
|
||||
UnregisterSelectionCallback(mailItem);
|
||||
|
||||
UnregisterPropertyChangedCallback(IsSelectedProperty, _selectionChangeCallbackToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterSelectionCallback(IMailListItem mailItem)
|
||||
{
|
||||
mailItem.PropertyChanged -= MailPropChanged;
|
||||
}
|
||||
|
||||
private void RegisterSelectionCallback(IMailListItem mailItem)
|
||||
{
|
||||
mailItem.PropertyChanged -= MailPropChanged;
|
||||
mailItem.PropertyChanged += MailPropChanged;
|
||||
}
|
||||
|
||||
// From model
|
||||
private void MailPropChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is not MailItemViewModel mailItem) return;
|
||||
|
||||
if (e.PropertyName == nameof(MailItemViewModel.IsSelected)) ApplySelectionForContainer(mailItem);
|
||||
}
|
||||
|
||||
// From container.
|
||||
private void OnIsSelectedChanged(DependencyObject sender, DependencyProperty dp)
|
||||
{
|
||||
if (Content is IMailListItem mailItem)
|
||||
{
|
||||
ApplySelectionForModel(mailItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySelectionForModel(IMailListItem mailItem)
|
||||
{
|
||||
mailItem.IsSelected = IsSelected;
|
||||
WeakReferenceMessenger.Default.Send(new SelectedItemsChangedMessage());
|
||||
}
|
||||
|
||||
private void ApplySelectionForContainer(IMailListItem mailItem)
|
||||
{
|
||||
if (IsSelected != mailItem.IsSelected)
|
||||
{
|
||||
IsSelected = mailItem.IsSelected;
|
||||
}
|
||||
if (newContent is MailItemViewModel mailItemViewModel) Item = mailItemViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Wino.Controls;
|
||||
@@ -10,30 +10,29 @@ namespace Wino.Mail.WinUI.Controls.ListView;
|
||||
|
||||
public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
|
||||
{
|
||||
public bool IsThreadExpanded
|
||||
[GeneratedDependencyProperty]
|
||||
public partial bool IsThreadExpanded { get; set; }
|
||||
|
||||
[GeneratedDependencyProperty]
|
||||
public partial ThreadMailItemViewModel? Item { get; set; }
|
||||
|
||||
protected override void OnContentChanged(object oldContent, object newContent)
|
||||
{
|
||||
get { return (bool)GetValue(IsThreadExpandedProperty); }
|
||||
set { SetValue(IsThreadExpandedProperty, value); }
|
||||
base.OnContentChanged(oldContent, newContent);
|
||||
|
||||
if (newContent is ThreadMailItemViewModel threadMailItemViewModel) Item = threadMailItemViewModel;
|
||||
}
|
||||
|
||||
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()
|
||||
partial void OnIsThreadExpandedChanged(bool newValue)
|
||||
{
|
||||
if (Content is ThreadMailItemViewModel mailItem)
|
||||
{
|
||||
UnregisterSelectionCallback(mailItem);
|
||||
|
||||
UnregisterPropertyChangedCallback(IsSelectedProperty, _selectionChangeCallbackToken);
|
||||
}
|
||||
// 1. Reflect expansion changes to WinoExpander.
|
||||
// 2. Automatically select first item on expansion, if none selected.
|
||||
// 3. Unselect all items on collapse.
|
||||
}
|
||||
|
||||
private static void OnIsThreadExpandedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs dp)
|
||||
@@ -42,79 +41,32 @@ public partial class WinoThreadMailItemViewModelListViewItem : ListViewItem
|
||||
// 2. Automatically select first item on expansion, if none selected.
|
||||
// 3. Unselect all items on collapse.
|
||||
|
||||
var control = sender as WinoThreadMailItemViewModelListViewItem;
|
||||
//var control = sender as WinoThreadMailItemViewModelListViewItem;
|
||||
|
||||
var innerControl = control?.GetWinoListViewControl();
|
||||
var expander = control?.GetExpander();
|
||||
//var innerControl = control?.GetWinoListViewControl();
|
||||
//var expander = control?.GetExpander();
|
||||
|
||||
if (innerControl == null || control == null || expander == null) return;
|
||||
//if (innerControl == null || control == null || expander == null) return;
|
||||
|
||||
|
||||
// 2
|
||||
if (control.IsThreadExpanded && innerControl.SelectedItems.Count == 0 && innerControl.Items.Count > 0)
|
||||
{
|
||||
innerControl.SelectedItems.Clear();
|
||||
//// 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.
|
||||
// // 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;
|
||||
// var firstItem = innerControl.Items.FirstOrDefault() as MailItemViewModel;
|
||||
|
||||
firstItem?.IsSelected = true;
|
||||
}
|
||||
// firstItem?.IsSelected = true;
|
||||
//}
|
||||
|
||||
// 1
|
||||
expander.IsExpanded = control.IsThreadExpanded;
|
||||
//// 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;
|
||||
//// 3
|
||||
//if (!control.IsSelected) innerControl?.SelectedItems.Clear();
|
||||
}
|
||||
|
||||
public WinoListView? GetWinoListViewControl()
|
||||
|
||||
@@ -69,7 +69,10 @@
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="ThreadMailItemTemplate" x:DataType="viewModelData:ThreadMailItemViewModel">
|
||||
<controls:WinoExpander x:Name="ExpanderPart" IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
|
||||
<controls:WinoExpander
|
||||
x:Name="ExpanderPart"
|
||||
Margin="0,2"
|
||||
IsExpanded="{x:Bind IsThreadExpanded, Mode=TwoWay}">
|
||||
<controls:WinoExpander.Header>
|
||||
<controls:MailItemDisplayInformationControl
|
||||
x:DefaultBindMode="OneWay"
|
||||
@@ -95,11 +98,20 @@
|
||||
toolkitExt:ListViewExtensions.ItemContainerStretchDirection="Horizontal"
|
||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
||||
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
||||
IsItemClickEnabled="True"
|
||||
IsThreadListView="True"
|
||||
ItemClick="WinoListViewItemClicked"
|
||||
ItemContainerStyle="{StaticResource DefaultMailListViewItemStyle}"
|
||||
ItemTemplate="{StaticResource SingleMailItemTemplate}"
|
||||
ItemsSource="{x:Bind ThreadEmails, Mode=OneTime}"
|
||||
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
||||
SelectionMode="Extended" />
|
||||
SelectionMode="Extended">
|
||||
<listview:WinoListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Margin="8,0,12,0" AreStickyGroupHeadersEnabled="True" />
|
||||
</ItemsPanelTemplate>
|
||||
</listview:WinoListView.ItemsPanel>
|
||||
</listview:WinoListView>
|
||||
</controls:WinoExpander.Content>
|
||||
</controls:WinoExpander>
|
||||
|
||||
@@ -117,7 +129,7 @@
|
||||
|
||||
<DataTemplate x:Key="MailGroupHeaderDefaultTemplate" x:DataType="collections:IReadOnlyObservableGroup">
|
||||
<Grid
|
||||
Margin="4,2"
|
||||
Margin="0,0,0,6"
|
||||
AllowFocusOnInteraction="False"
|
||||
Background="{ThemeResource MailListHeaderBackgroundColor}"
|
||||
CornerRadius="6">
|
||||
@@ -386,12 +398,11 @@
|
||||
toolkitExt:ScrollViewerExtensions.VerticalScrollBarMargin="0"
|
||||
ChoosingItemContainer="WinoListViewChoosingItemContainer"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="WinoListView_ItemClick"
|
||||
ItemClick="WinoListViewItemClicked"
|
||||
ItemContainerStyleSelector="{StaticResource WinoMailItemContainerStyleSelector}"
|
||||
ItemTemplateSelector="{StaticResource MailItemTemplateSelector}"
|
||||
ItemsSource="{x:Bind MailCollectionViewSource.View, Mode=OneWay}"
|
||||
ProcessKeyboardAccelerators="WinoListViewProcessKeyboardAccelerators"
|
||||
SelectionChanged="WinoListViewSelectionChanged"
|
||||
SelectionMode="Extended">
|
||||
<listview:WinoListView.ItemContainerTransitions>
|
||||
<TransitionCollection>
|
||||
@@ -400,7 +411,7 @@
|
||||
</listview:WinoListView.ItemContainerTransitions>
|
||||
<listview:WinoListView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsStackPanel Margin="8,0,12,0" AreStickyGroupHeadersEnabled="True" />
|
||||
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" GroupPadding="10,10,10,0" />
|
||||
</ItemsPanelTemplate>
|
||||
</listview:WinoListView.ItemsPanel>
|
||||
<listview:WinoListView.Resources>
|
||||
@@ -412,15 +423,6 @@
|
||||
<GroupStyle HeaderTemplate="{StaticResource MailGroupHeaderDefaultTemplate}" HidesIfEmpty="True" />
|
||||
</listview:WinoListView.GroupStyle>
|
||||
</listview:WinoListView>
|
||||
<!--<advanced:WinoItemsView
|
||||
x:Name="MailListView"
|
||||
Margin="4"
|
||||
ItemTemplate="{StaticResource MailItemContainerSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.MailCollection.Items, Mode=OneTime}"
|
||||
Layout="{StaticResource DefaultItemsViewLayout}"
|
||||
LoadMoreCommand="{x:Bind ViewModel.LoadMoreItemsCommand}"
|
||||
ProcessKeyboardAccelerators="MailListView_ProcessKeyboardAccelerators"
|
||||
SelectionMode="Extended" />-->
|
||||
|
||||
<!-- Try online search panel. -->
|
||||
<Grid Grid.Row="1" Visibility="{x:Bind ViewModel.IsOnlineSearchButtonVisible, Mode=OneWay}">
|
||||
|
||||
@@ -63,6 +63,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
@@ -162,6 +163,8 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
args.IsContainerPrepared = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async void MailItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
|
||||
{
|
||||
// TODO: New ItemsView implementation.
|
||||
@@ -496,7 +499,7 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
|
||||
private void UpdateAdaptiveness()
|
||||
{
|
||||
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled || KeyPressService.IsCtrlKeyPressed();
|
||||
bool isMultiSelectionEnabled = ViewModel.IsMultiSelectionModeEnabled;
|
||||
|
||||
if (StatePersistenceService.IsReaderNarrowed)
|
||||
{
|
||||
@@ -545,22 +548,85 @@ public sealed partial class MailListPage : MailListPageAbstract,
|
||||
}
|
||||
}
|
||||
|
||||
private void WinoListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
private async void WinoListViewItemClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (sender is not WinoListView listView) return;
|
||||
|
||||
}
|
||||
bool isSelectedItemFromThread = listView.IsThreadListView;
|
||||
bool isCtrlPressed = KeyPressService.IsCtrlKeyPressed();
|
||||
|
||||
private void WinoListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ThreadMailItemViewModel clickedThread)
|
||||
bool isClickingThreadItem = e.ClickedItem is ThreadMailItemViewModel;
|
||||
|
||||
// Unselect all items. It's single selection.
|
||||
if (!isCtrlPressed)
|
||||
{
|
||||
// Only if container is selected.
|
||||
await ViewModel.MailCollection.UnselectAllAsync();
|
||||
|
||||
var threadContainer = MailListView.ContainerFromItem(clickedThread) as WinoThreadMailItemViewModelListViewItem;
|
||||
|
||||
if (threadContainer?.IsSelected ?? false)
|
||||
if (!isSelectedItemFromThread && !isClickingThreadItem)
|
||||
{
|
||||
clickedThread.IsThreadExpanded = !clickedThread.IsThreadExpanded;
|
||||
await ViewModel.MailCollection.CollapseAllThreadsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.ClickedItem is MailItemViewModel mailListItem)
|
||||
{
|
||||
mailListItem.IsSelected = !mailListItem.IsSelected;
|
||||
}
|
||||
else if (e.ClickedItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
// Extended selection mode handling for threads
|
||||
if (isCtrlPressed)
|
||||
{
|
||||
// If thread is selected and Ctrl is pressed
|
||||
if (threadMailItemViewModel.IsSelected)
|
||||
{
|
||||
// If thread was collapsed, expand it
|
||||
if (!threadMailItemViewModel.IsThreadExpanded)
|
||||
{
|
||||
threadMailItemViewModel.IsThreadExpanded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if all items are selected.
|
||||
// If so, then unselect all items in the thread and unselect the thread itself.
|
||||
if (threadMailItemViewModel.ThreadEmails.All(a => a.IsSelected))
|
||||
{
|
||||
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||
{
|
||||
threadEmail.IsSelected = false;
|
||||
}
|
||||
threadMailItemViewModel.IsSelected = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If thread was already expanded, select all items in the thread
|
||||
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||
{
|
||||
threadEmail.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Thread is not selected, select and expand it.
|
||||
if (!threadMailItemViewModel.IsThreadExpanded) threadMailItemViewModel.IsThreadExpanded = true;
|
||||
if (!threadMailItemViewModel.IsSelected)
|
||||
{
|
||||
threadMailItemViewModel.IsSelected = true;
|
||||
|
||||
foreach (var threadEmail in threadMailItemViewModel.ThreadEmails)
|
||||
{
|
||||
threadEmail.IsSelected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Ctrl pressed, toggle expansion state (default behavior)
|
||||
threadMailItemViewModel.IsThreadExpanded = !threadMailItemViewModel.IsThreadExpanded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user