Initial commit.
This commit is contained in:
110
Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml
Normal file
110
Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml
Normal file
@@ -0,0 +1,110 @@
|
||||
<UserControl
|
||||
x:Class="Wino.Controls.Advanced.WinoAppTitleBar"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:advanced="using:Wino.Controls.Advanced"
|
||||
xmlns:animatedvisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
|
||||
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Wino.Controls.Advanced"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid x:Name="MainGrid" Background="{ThemeResource AppBarBackgroundColor}">
|
||||
<Grid
|
||||
x:Name="dragbar"
|
||||
Background="Transparent"
|
||||
IsHitTestVisible="True" />
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="FluentButtonWidth" Width="Auto" />
|
||||
<ColumnDefinition x:Name="EmptySpaceWidth" Width="*" />
|
||||
<ColumnDefinition x:Name="SystemReservedWidth" Width="180" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Shell Title Bar Left Side Background Placeholder -->
|
||||
<Grid Grid.ColumnSpan="3" />
|
||||
|
||||
<!-- Menu + Back Button -->
|
||||
<StackPanel
|
||||
x:Name="LeftMenuStackPanel"
|
||||
Orientation="Horizontal"
|
||||
SizeChanged="asd">
|
||||
<Button
|
||||
x:Name="PaneButton"
|
||||
Width="48"
|
||||
Margin="-2,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Click="PaneClicked">
|
||||
<muxc:AnimatedIcon Width="16">
|
||||
<muxc:AnimatedIcon.Source>
|
||||
<animatedvisuals:AnimatedGlobalNavigationButtonVisualSource />
|
||||
</muxc:AnimatedIcon.Source>
|
||||
<muxc:AnimatedIcon.FallbackIconSource>
|
||||
<muxc:SymbolIconSource Symbol="GlobalNavigationButton" />
|
||||
</muxc:AnimatedIcon.FallbackIconSource>
|
||||
</muxc:AnimatedIcon>
|
||||
</Button>
|
||||
<Button
|
||||
x:Name="BackButton"
|
||||
Width="48"
|
||||
Margin="-2,4,4,4"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Click="BackClicked"
|
||||
Visibility="{x:Bind IsBackButtonVisible, Mode=OneWay}">
|
||||
<muxc:AnimatedIcon Width="16">
|
||||
<muxc:AnimatedIcon.Source>
|
||||
<animatedvisuals:AnimatedBackVisualSource />
|
||||
</muxc:AnimatedIcon.Source>
|
||||
<muxc:AnimatedIcon.FallbackIconSource>
|
||||
<muxc:SymbolIconSource Symbol="Back" />
|
||||
</muxc:AnimatedIcon.FallbackIconSource>
|
||||
</muxc:AnimatedIcon>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- CoreWindow Title -->
|
||||
<TextBlock
|
||||
x:Name="CoreWindowTitleTextBlock"
|
||||
Grid.Column="1"
|
||||
Margin="4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalTextAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind CoreWindowText, Mode=OneWay}"
|
||||
TextTrimming="Clip">
|
||||
<animations:Implicit.ShowAnimations>
|
||||
<animations:OpacityAnimation
|
||||
From="0"
|
||||
To="1.0"
|
||||
Duration="0:0:1" />
|
||||
</animations:Implicit.ShowAnimations>
|
||||
</TextBlock>
|
||||
|
||||
<Grid
|
||||
x:Name="ContentGrid"
|
||||
Grid.Column="1"
|
||||
Background="Transparent">
|
||||
<!-- Shell Sub Content -->
|
||||
<ContentPresenter Canvas.ZIndex="2" Content="{x:Bind ShellFrameContent, Mode=OneWay}">
|
||||
<ContentPresenter.ContentTransitions>
|
||||
<TransitionCollection>
|
||||
<PaneThemeTransition Edge="Top" />
|
||||
</TransitionCollection>
|
||||
</ContentPresenter.ContentTransitions>
|
||||
</ContentPresenter>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
169
Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml.cs
Normal file
169
Wino.Mail/Controls/Advanced/WinoAppTitleBar.xaml.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using Windows.Foundation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace Wino.Controls.Advanced
|
||||
{
|
||||
public sealed partial class WinoAppTitleBar : UserControl
|
||||
{
|
||||
public event TypedEventHandler<WinoAppTitleBar, RoutedEventArgs> BackButtonClicked;
|
||||
|
||||
public string CoreWindowText
|
||||
{
|
||||
get { return (string)GetValue(CoreWindowTextProperty); }
|
||||
set { SetValue(CoreWindowTextProperty, value); }
|
||||
}
|
||||
|
||||
public double SystemReserved
|
||||
{
|
||||
get { return (double)GetValue(SystemReservedProperty); }
|
||||
set { SetValue(SystemReservedProperty, value); }
|
||||
}
|
||||
|
||||
public UIElement ShellFrameContent
|
||||
{
|
||||
get { return (UIElement)GetValue(ShellFrameContentProperty); }
|
||||
set { SetValue(ShellFrameContentProperty, value); }
|
||||
}
|
||||
|
||||
public Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode NavigationViewDisplayMode
|
||||
{
|
||||
get { return (Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode)GetValue(NavigationViewDisplayModeProperty); }
|
||||
set { SetValue(NavigationViewDisplayModeProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsNavigationPaneOpen
|
||||
{
|
||||
get { return (bool)GetValue(IsNavigationPaneOpenProperty); }
|
||||
set { SetValue(IsNavigationPaneOpenProperty, value); }
|
||||
}
|
||||
|
||||
public double OpenPaneLength
|
||||
{
|
||||
get { return (double)GetValue(OpenPaneLengthProperty); }
|
||||
set { SetValue(OpenPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsBackButtonVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsBackButtonVisibleProperty); }
|
||||
set { SetValue(IsBackButtonVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsReaderNarrowed
|
||||
{
|
||||
get { return (bool)GetValue(IsReaderNarrowedProperty); }
|
||||
set { SetValue(IsReaderNarrowedProperty, value); }
|
||||
}
|
||||
|
||||
public bool IsRenderingPaneVisible
|
||||
{
|
||||
get { return (bool)GetValue(IsRenderingPaneVisibleProperty); }
|
||||
set { SetValue(IsRenderingPaneVisibleProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsRenderingPaneVisibleProperty = DependencyProperty.Register(nameof(IsRenderingPaneVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsReaderNarrowedProperty = DependencyProperty.Register(nameof(IsReaderNarrowed), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnIsReaderNarrowedChanged));
|
||||
public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty OpenPaneLengthProperty = DependencyProperty.Register(nameof(OpenPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0d, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty IsNavigationPaneOpenProperty = DependencyProperty.Register(nameof(IsNavigationPaneOpen), typeof(bool), typeof(WinoAppTitleBar), new PropertyMetadata(false, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty NavigationViewDisplayModeProperty = DependencyProperty.Register(nameof(NavigationViewDisplayMode), typeof(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode), typeof(WinoAppTitleBar), new PropertyMetadata(Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ShellFrameContentProperty = DependencyProperty.Register(nameof(ShellFrameContent), typeof(UIElement), typeof(WinoAppTitleBar), new PropertyMetadata(null, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty SystemReservedProperty = DependencyProperty.Register(nameof(SystemReserved), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(0, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty CoreWindowTextProperty = DependencyProperty.Register(nameof(CoreWindowText), typeof(string), typeof(WinoAppTitleBar), new PropertyMetadata(string.Empty, OnDrawingPropertyChanged));
|
||||
public static readonly DependencyProperty ReadingPaneLengthProperty = DependencyProperty.Register(nameof(ReadingPaneLength), typeof(double), typeof(WinoAppTitleBar), new PropertyMetadata(420d, OnDrawingPropertyChanged));
|
||||
|
||||
|
||||
public double ReadingPaneLength
|
||||
{
|
||||
get { return (double)GetValue(ReadingPaneLengthProperty); }
|
||||
set { SetValue(ReadingPaneLengthProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnIsReaderNarrowedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
bar.DrawTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnDrawingPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
if (obj is WinoAppTitleBar bar)
|
||||
{
|
||||
bar.DrawTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTitleBar()
|
||||
{
|
||||
UpdateLayout();
|
||||
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Collapsed;
|
||||
ContentGrid.Width = double.NaN;
|
||||
ContentGrid.Margin = new Thickness(0, 0, 0, 0);
|
||||
ContentGrid.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
|
||||
EmptySpaceWidth.Width = new GridLength(1, GridUnitType.Star);
|
||||
|
||||
// Menu is not visible.
|
||||
if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Minimal)
|
||||
{
|
||||
|
||||
}
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Compact)
|
||||
{
|
||||
// Icons are visible.
|
||||
|
||||
if (!IsReaderNarrowed)
|
||||
{
|
||||
ContentGrid.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
ContentGrid.Width = ReadingPaneLength;
|
||||
}
|
||||
}
|
||||
else if (NavigationViewDisplayMode == Microsoft.UI.Xaml.Controls.NavigationViewDisplayMode.Expanded)
|
||||
{
|
||||
if (IsNavigationPaneOpen)
|
||||
{
|
||||
CoreWindowTitleTextBlock.Visibility = Visibility.Visible;
|
||||
|
||||
// LMargin = OpenPaneLength - LeftMenuStackPanel
|
||||
ContentGrid.Margin = new Thickness(OpenPaneLength - LeftMenuStackPanel.ActualSize.X, 0, 0, 0);
|
||||
|
||||
if (!IsReaderNarrowed)
|
||||
{
|
||||
ContentGrid.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
ContentGrid.Width = ReadingPaneLength;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EmptySpaceWidth.Width = new GridLength(ReadingPaneLength, GridUnitType.Pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WinoAppTitleBar()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Window.Current.SetTitleBar(dragbar);
|
||||
}
|
||||
|
||||
private void BackClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BackButtonClicked?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void PaneClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
IsNavigationPaneOpen = !IsNavigationPaneOpen;
|
||||
}
|
||||
|
||||
private void asd(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
DrawTitleBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
382
Wino.Mail/Controls/Advanced/WinoListView.cs
Normal file
382
Wino.Mail/Controls/Advanced/WinoListView.cs
Normal file
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using MoreLinq;
|
||||
using Serilog;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Models.MailItem;
|
||||
using Wino.Extensions;
|
||||
using Wino.Mail.ViewModels.Data;
|
||||
using Wino.Mail.ViewModels.Messages;
|
||||
|
||||
namespace Wino.Controls.Advanced
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom ListView control that handles multiple selection with Extended/Multiple selection mode
|
||||
/// and supports threads.
|
||||
/// </summary>
|
||||
public class WinoListView : ListView, IDisposable
|
||||
{
|
||||
private ILogger logger = Log.ForContext<WinoListView>();
|
||||
|
||||
private const string PART_ScrollViewer = "ScrollViewer";
|
||||
private ScrollViewer internalScrollviewer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this ListView belongs to thread items.
|
||||
/// This is important for detecting selected items etc.
|
||||
/// </summary>
|
||||
public bool IsThreadListView
|
||||
{
|
||||
get { return (bool)GetValue(IsThreadListViewProperty); }
|
||||
set { SetValue(IsThreadListViewProperty, value); }
|
||||
}
|
||||
|
||||
public ICommand ItemDeletedCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(ItemDeletedCommandProperty); }
|
||||
set { SetValue(ItemDeletedCommandProperty, value); }
|
||||
}
|
||||
|
||||
public ICommand LoadMoreCommand
|
||||
{
|
||||
get { return (ICommand)GetValue(LoadMoreCommandProperty); }
|
||||
set { SetValue(LoadMoreCommandProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LoadMoreCommandProperty = DependencyProperty.Register(nameof(LoadMoreCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null));
|
||||
public static readonly DependencyProperty IsThreadListViewProperty = DependencyProperty.Register(nameof(IsThreadListView), typeof(bool), typeof(WinoListView), new PropertyMetadata(false));
|
||||
public static readonly DependencyProperty ItemDeletedCommandProperty = DependencyProperty.Register(nameof(ItemDeletedCommand), typeof(ICommand), typeof(WinoListView), new PropertyMetadata(null));
|
||||
|
||||
public WinoListView()
|
||||
{
|
||||
CanDragItems = true;
|
||||
IsItemClickEnabled = true;
|
||||
IsMultiSelectCheckBoxEnabled = true;
|
||||
IsRightTapEnabled = true;
|
||||
SelectionMode = ListViewSelectionMode.Extended;
|
||||
ShowsScrollingPlaceholders = false;
|
||||
SingleSelectionFollowsFocus = true;
|
||||
|
||||
DragItemsCompleted += ItemDragCompleted;
|
||||
DragItemsStarting += ItemDragStarting;
|
||||
SelectionChanged += SelectedItemsChanged;
|
||||
ItemClick += MailItemClicked;
|
||||
ProcessKeyboardAccelerators += ProcessDelKey;
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
internalScrollviewer = GetTemplateChild(PART_ScrollViewer) as ScrollViewer;
|
||||
|
||||
if (internalScrollviewer == null)
|
||||
{
|
||||
logger.Warning("WinoListView does not have an internal ScrollViewer. Infinite scrolling behavior might be effected.");
|
||||
return;
|
||||
}
|
||||
|
||||
internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged;
|
||||
internalScrollviewer.ViewChanged += InternalScrollVeiwerViewChanged;
|
||||
}
|
||||
|
||||
private double lastestRaisedOffset = 0;
|
||||
private int lastItemSize = 0;
|
||||
|
||||
// TODO: This is buggy. Does not work all the time. Debug.
|
||||
|
||||
private void InternalScrollVeiwerViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
|
||||
{
|
||||
if (internalScrollviewer == null) return;
|
||||
|
||||
// No need to raise init request if there are no items in the list.
|
||||
if (Items.Count == 0) return;
|
||||
|
||||
// If the scrolling is finished, check the current viewport height.
|
||||
if (e.IsIntermediate)
|
||||
{
|
||||
var currentOffset = internalScrollviewer.VerticalOffset;
|
||||
var maxOffset = internalScrollviewer.ScrollableHeight;
|
||||
|
||||
if (currentOffset + 10 >= maxOffset && lastestRaisedOffset != maxOffset && Items.Count != lastItemSize)
|
||||
{
|
||||
// We must load more.
|
||||
lastestRaisedOffset = maxOffset;
|
||||
lastItemSize = Items.Count;
|
||||
|
||||
LoadMoreCommand?.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDelKey(UIElement sender, Windows.UI.Xaml.Input.ProcessKeyboardAcceleratorEventArgs args)
|
||||
{
|
||||
if (args.Key == Windows.System.VirtualKey.Delete)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
ItemDeletedCommand?.Execute((int)MailOperation.SoftDelete);
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemDragCompleted(ListViewBase sender, DragItemsCompletedEventArgs args)
|
||||
{
|
||||
if (args.Items.Any(a => a is MailItemViewModel))
|
||||
{
|
||||
args.Items.Cast<MailItemViewModel>().ForEach(a => a.IsCustomFocused = false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemDragStarting(object sender, DragItemsStartingEventArgs args)
|
||||
{
|
||||
// Dragging multiple mails from different accounts/folders are supported with the condition below:
|
||||
// All mails belongs to the drag will be matched on the dropped folder's account.
|
||||
// Meaning that if users drag 1 mail from Account A/Inbox and 1 mail from Account B/Inbox,
|
||||
// and drop to Account A/Inbox, the mail from Account B/Inbox will NOT be moved.
|
||||
|
||||
if (IsThreadListView)
|
||||
{
|
||||
var allItems = args.Items.Cast<MailItemViewModel>();
|
||||
|
||||
// Highlight all items
|
||||
allItems.ForEach(a => a.IsCustomFocused = true);
|
||||
|
||||
// Set native drag arg properties.
|
||||
|
||||
var dragPackage = new MailDragPackage(allItems.Cast<IMailItem>());
|
||||
|
||||
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dragPackage = new MailDragPackage(args.Items.Cast<IMailItem>());
|
||||
|
||||
args.Data.Properties.Add(nameof(MailDragPackage), dragPackage);
|
||||
}
|
||||
}
|
||||
|
||||
private void MailItemClicked(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ThreadMailItemViewModel clickedThread)
|
||||
{
|
||||
clickedThread.IsThreadExpanded = !clickedThread.IsThreadExpanded;
|
||||
|
||||
if (!clickedThread.IsThreadExpanded)
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeSelectionMode(ListViewSelectionMode selectionMode)
|
||||
{
|
||||
SelectionMode = selectionMode;
|
||||
|
||||
if (!IsThreadListView)
|
||||
{
|
||||
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
|
||||
{
|
||||
var threadListView = GetThreadInternalListView(c);
|
||||
|
||||
if (threadListView != null)
|
||||
{
|
||||
threadListView.SelectionMode = selectionMode;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the container for given mail item and adds it to selected items.
|
||||
/// </summary>
|
||||
/// <param name="mailItemViewModel">Mail to be added to selected items.</param>
|
||||
/// <returns>Whether selection was successful or not.</returns>
|
||||
public bool SelectMailItemContainer(MailItemViewModel mailItemViewModel)
|
||||
{
|
||||
var itemContainer = ContainerFromItem(mailItemViewModel);
|
||||
|
||||
// This item might be in thread container.
|
||||
if (itemContainer == null)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
|
||||
{
|
||||
if (!found)
|
||||
{
|
||||
var threadListView = GetThreadInternalListView(c);
|
||||
|
||||
if (threadListView != null)
|
||||
found = threadListView.SelectMailItemContainer(mailItemViewModel);
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
SelectedItems.Add(mailItemViewModel);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively clears all selections except the given mail.
|
||||
/// </summary>
|
||||
/// <param name="exceptViewModel">Exceptional mail item to be not unselected.</param>
|
||||
/// <param name="preserveThreadExpanding">Whether expansion states of thread containers should stay as it is or not.</param>
|
||||
public void ClearSelections(MailItemViewModel exceptViewModel = null, bool preserveThreadExpanding = false)
|
||||
{
|
||||
SelectedItems.Clear();
|
||||
|
||||
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
|
||||
{
|
||||
var threadListView = GetThreadInternalListView(c);
|
||||
|
||||
if (threadListView == null)
|
||||
return;
|
||||
|
||||
if (exceptViewModel != null)
|
||||
{
|
||||
if (!threadListView.SelectedItems.Contains(exceptViewModel))
|
||||
{
|
||||
if (!preserveThreadExpanding)
|
||||
{
|
||||
c.IsThreadExpanded = false;
|
||||
}
|
||||
|
||||
threadListView.SelectedItems.Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!preserveThreadExpanding)
|
||||
{
|
||||
c.IsThreadExpanded = false;
|
||||
}
|
||||
|
||||
threadListView.SelectedItems.Clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively selects all mails, including thread items.
|
||||
/// </summary>
|
||||
public void SelectAllWino()
|
||||
{
|
||||
SelectAll();
|
||||
|
||||
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
|
||||
{
|
||||
c.IsThreadExpanded = true;
|
||||
|
||||
var threadListView = GetThreadInternalListView(c);
|
||||
|
||||
threadListView?.SelectAll();
|
||||
});
|
||||
}
|
||||
|
||||
// SelectedItems changed.
|
||||
private void SelectedItemsChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.RemovedItems != null)
|
||||
{
|
||||
foreach (var removedItem in e.RemovedItems)
|
||||
{
|
||||
if (removedItem is MailItemViewModel removedMailItemViewModel)
|
||||
{
|
||||
// Mail item un-selected.
|
||||
|
||||
removedMailItemViewModel.IsSelected = false;
|
||||
WeakReferenceMessenger.Default.Send(new MailItemSelectionRemovedEvent(removedMailItemViewModel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.AddedItems != null)
|
||||
{
|
||||
foreach (var addedItem in e.AddedItems)
|
||||
{
|
||||
if (addedItem is MailItemViewModel addedMailItemViewModel)
|
||||
{
|
||||
// Mail item selected.
|
||||
|
||||
addedMailItemViewModel.IsSelected = true;
|
||||
|
||||
WeakReferenceMessenger.Default.Send(new MailItemSelectedEvent(addedMailItemViewModel));
|
||||
}
|
||||
else if (addedItem is ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
threadMailItemViewModel.IsThreadExpanded = true;
|
||||
|
||||
// Don't select thread containers.
|
||||
SelectedItems.Remove(addedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsThreadListView)
|
||||
{
|
||||
if (SelectionMode == ListViewSelectionMode.Extended && SelectedItems.Count == 1)
|
||||
{
|
||||
// Only 1 single item is selected in extended mode for main list view.
|
||||
// We should un-select all thread items.
|
||||
|
||||
Items.Where(a => a is ThreadMailItemViewModel).Cast<ThreadMailItemViewModel>().ForEach(c =>
|
||||
{
|
||||
// c.IsThreadExpanded = false;
|
||||
|
||||
var threadListView = GetThreadInternalListView(c);
|
||||
|
||||
threadListView?.SelectedItems.Clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SelectionMode == ListViewSelectionMode.Extended && SelectedItems.Count == 1)
|
||||
{
|
||||
// Tell main list view to unselect all his items.
|
||||
|
||||
if (SelectedItems[0] is MailItemViewModel selectedMailItemViewModel)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new ResetSingleMailItemSelectionEvent(selectedMailItemViewModel));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private WinoListView GetThreadInternalListView(ThreadMailItemViewModel threadMailItemViewModel)
|
||||
{
|
||||
var itemContainer = ContainerFromItem(threadMailItemViewModel);
|
||||
|
||||
if (itemContainer is ListViewItem listItem)
|
||||
{
|
||||
var expander = listItem.GetChildByName<Expander>("ThreadExpander");
|
||||
|
||||
if (expander != null)
|
||||
return expander.Content as WinoListView;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DragItemsCompleted -= ItemDragCompleted;
|
||||
DragItemsStarting -= ItemDragStarting;
|
||||
SelectionChanged -= SelectedItemsChanged;
|
||||
ItemClick -= MailItemClicked;
|
||||
ProcessKeyboardAccelerators -= ProcessDelKey;
|
||||
|
||||
if (internalScrollviewer != null)
|
||||
{
|
||||
internalScrollviewer.ViewChanged -= InternalScrollVeiwerViewChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user