From 057edb54889fc06e6d006899c67397deb637ac43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Tue, 21 Oct 2025 01:27:29 +0200 Subject: [PATCH] Custom print dialog and better message registrations --- Wino.Calendar/Views/AppShell.xaml.cs | 14 + .../Interfaces/IDialogServiceBase.cs | 2 + .../Models/MailItem/MailDragPackage.cs | 4 +- .../Printing/WebView2PrintSettingsModel.cs | 377 ++++-------------- Wino.Core.ViewModels/CalendarBaseViewModel.cs | 14 + Wino.Core.ViewModels/CoreBaseViewModel.cs | 20 +- Wino.Core.WinUI/BasePage.cs | 21 +- Wino.Core.WinUI/Dialogs/PrintDialog.xaml | 262 +++--------- Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs | 161 +++++--- Wino.Core.WinUI/Helpers/XamlHelpers.cs | 2 +- .../Interfaces/IPrintDialogService.cs | 30 -- Wino.Core.WinUI/Services/DialogServiceBase.cs | 35 ++ .../Services/PrintDialogService.cs | 93 ----- .../Views/Abstract/SettingsPageBase.cs | 2 +- Wino.Core.WinUI/Views/SettingsPage.xaml.cs | 20 +- Wino.Mail.ViewModels/AppShellViewModel.cs | 32 ++ Wino.Mail.ViewModels/MailBaseViewModel.cs | 30 ++ Wino.Mail.ViewModels/MailListPageViewModel.cs | 30 ++ .../MailRenderingPageViewModel.cs | 49 ++- Wino.Mail.WinUI/App.xaml.cs | 11 +- Wino.Mail.WinUI/AppShell.xaml.cs | 58 ++- .../Dialogs/NewImapSetupDialog.xaml.cs | 14 +- Wino.Mail.WinUI/ShellWindow.xaml.cs | 19 +- Wino.Mail.WinUI/Views/MailListPage.xaml.cs | 26 +- .../Views/MailRenderingPage.xaml.cs | 56 ++- Wino.Mail/AppShell.xaml.cs | 20 + Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs | 14 +- 27 files changed, 656 insertions(+), 760 deletions(-) delete mode 100644 Wino.Core.WinUI/Interfaces/IPrintDialogService.cs delete mode 100644 Wino.Core.WinUI/Services/PrintDialogService.cs diff --git a/Wino.Calendar/Views/AppShell.xaml.cs b/Wino.Calendar/Views/AppShell.xaml.cs index b68a2ca9..bfe5fa0f 100644 --- a/Wino.Calendar/Views/AppShell.xaml.cs +++ b/Wino.Calendar/Views/AppShell.xaml.cs @@ -50,4 +50,18 @@ public sealed partial class AppShell : AppShellAbstract, private void AppBarBackButtonClicked(Core.UWP.Controls.WinoAppTitleBar sender, RoutedEventArgs args) => ViewModel.NavigationService.GoBack(); + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + WeakReferenceMessenger.Default.Unregister(this); + } } diff --git a/Wino.Core.Domain/Interfaces/IDialogServiceBase.cs b/Wino.Core.Domain/Interfaces/IDialogServiceBase.cs index 8cc97e6d..10189f83 100644 --- a/Wino.Core.Domain/Interfaces/IDialogServiceBase.cs +++ b/Wino.Core.Domain/Interfaces/IDialogServiceBase.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Wino.Core.Domain.Enums; using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Common; +using Wino.Core.Domain.Models.Printing; namespace Wino.Core.Domain.Interfaces; @@ -28,4 +29,5 @@ public interface IDialogServiceBase IAccountCreationDialog GetAccountCreationDialog(AccountCreationDialogResult accountCreationDialogResult); Task> PickFilesAsync(params object[] typeFilters); Task PickFilePathAsync(string saveFileName); + Task ShowPrintDialogAsync(WebView2PrintSettingsModel initialSettings = null); } diff --git a/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs b/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs index 50a4e40e..871aded8 100644 --- a/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs +++ b/Wino.Core.Domain/Models/MailItem/MailDragPackage.cs @@ -8,7 +8,7 @@ namespace Wino.Core.Domain.Models.MailItem; /// public class MailDragPackage { - public MailDragPackage(IEnumerable draggingMails) + public MailDragPackage(IEnumerable draggingMails) { DraggingMails = draggingMails; } @@ -21,5 +21,5 @@ public class MailDragPackage ]; } - public IEnumerable DraggingMails { get; set; } + public IEnumerable DraggingMails { get; set; } } diff --git a/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs b/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs index 9a2fdeaa..48455878 100644 --- a/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs +++ b/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using CommunityToolkit.Mvvm.ComponentModel; using Wino.Core.Domain.Enums; namespace Wino.Core.Domain.Models.Printing; @@ -6,339 +7,131 @@ namespace Wino.Core.Domain.Models.Printing; /// /// Wrapper model for CoreWebView2PrintSettings that provides bindable properties for UI controls. /// -public class WebView2PrintSettingsModel : INotifyPropertyChanged +public partial class WebView2PrintSettingsModel : ObservableObject { - private string _printerName = string.Empty; - private PrintOrientation _orientation = PrintOrientation.Portrait; - private PrintColorMode _colorMode = PrintColorMode.Color; - private PrintCollation _collation = PrintCollation.Default; - private PrintDuplex _duplex = PrintDuplex.Default; - private PrintMediaSize _mediaSize = PrintMediaSize.Default; - private int _copies = 1; - private double _marginTop = 1.0; - private double _marginBottom = 1.0; - private double _marginLeft = 1.0; - private double _marginRight = 1.0; - private bool _shouldPrintBackgrounds = false; - private bool _shouldPrintSelectionOnly = false; - private bool _shouldPrintHeaderAndFooter = true; - private string _headerTitle = string.Empty; - private string _footerUri = string.Empty; - private double _scaleFactor = 1.0; - private int _pagesPerSide = 1; - private string _pageRanges = string.Empty; + [ObservableProperty] + public partial string PrinterName { get; set; } = string.Empty; - public event PropertyChangedEventHandler PropertyChanged; + [ObservableProperty] + public partial PrintOrientation Orientation { get; set; } = PrintOrientation.Portrait; + + [ObservableProperty] + public partial PrintColorMode ColorMode { get; set; } = PrintColorMode.Color; + + [ObservableProperty] + public partial PrintCollation Collation { get; set; } = PrintCollation.Default; + + [ObservableProperty] + public partial PrintDuplex Duplex { get; set; } = PrintDuplex.Default; + + [ObservableProperty] + public partial PrintMediaSize MediaSize { get; set; } = PrintMediaSize.Default; + + [ObservableProperty] + public partial int Copies { get; set; } = 1; + + [ObservableProperty] + public partial double MarginTop { get; set; } = 1.0; + + [ObservableProperty] + public partial double MarginBottom { get; set; } = 1.0; + + [ObservableProperty] + public partial double MarginLeft { get; set; } = 1.0; + + [ObservableProperty] + public partial double MarginRight { get; set; } = 1.0; + + [ObservableProperty] + public partial bool ShouldPrintBackgrounds { get; set; } = false; + + [ObservableProperty] + public partial bool ShouldPrintSelectionOnly { get; set; } = false; + + [ObservableProperty] + public partial bool ShouldPrintHeaderAndFooter { get; set; } = true; + + [ObservableProperty] + public partial string HeaderTitle { get; set; } = string.Empty; + + [ObservableProperty] + public partial string FooterUri { get; set; } = string.Empty; + + [ObservableProperty] + public partial double ScaleFactor { get; set; } = 1.0; + + [ObservableProperty] + public partial int PagesPerSide { get; set; } = 1; + + [ObservableProperty] + public partial string PageRanges { get; set; } = string.Empty; /// - /// Name of the printer to use for printing. + /// Partial method for validation when Copies property changes. /// - public string PrinterName + partial void OnCopiesChanged(int value) { - get => _printerName; - set + if (value <= 0) { - if (_printerName != value) - { - _printerName = value; - OnPropertyChanged(nameof(PrinterName)); - } + Copies = 1; // Reset to minimum valid value } } /// - /// Orientation of the printed document. + /// Partial method for validation when ScaleFactor property changes. /// - public PrintOrientation Orientation + partial void OnScaleFactorChanged(double value) { - get => _orientation; - set + if (value < 0.1 || value > 2.0) { - if (_orientation != value) - { - _orientation = value; - OnPropertyChanged(nameof(Orientation)); - } + ScaleFactor = Math.Clamp(value, 0.1, 2.0); } } /// - /// Color mode for printing. + /// Partial method for validation when PagesPerSide property changes. /// - public PrintColorMode ColorMode + partial void OnPagesPerSideChanged(int value) { - get => _colorMode; - set + var validValues = new[] { 1, 2, 4, 6, 9, 16 }; + if (System.Array.IndexOf(validValues, value) < 0) { - if (_colorMode != value) - { - _colorMode = value; - OnPropertyChanged(nameof(ColorMode)); - } + PagesPerSide = 1; // Reset to default valid value } } /// - /// Collation setting for multiple copies. + /// Partial method for validation when margin properties change. /// - public PrintCollation Collation + partial void OnMarginTopChanged(double value) { - get => _collation; - set + if (value < 0) { - if (_collation != value) - { - _collation = value; - OnPropertyChanged(nameof(Collation)); - } + MarginTop = 0; } } - /// - /// Duplex printing mode. - /// - public PrintDuplex Duplex + partial void OnMarginBottomChanged(double value) { - get => _duplex; - set + if (value < 0) { - if (_duplex != value) - { - _duplex = value; - OnPropertyChanged(nameof(Duplex)); - } + MarginBottom = 0; } } - /// - /// Media size for printing. - /// - public PrintMediaSize MediaSize + partial void OnMarginLeftChanged(double value) { - get => _mediaSize; - set + if (value < 0) { - if (_mediaSize != value) - { - _mediaSize = value; - OnPropertyChanged(nameof(MediaSize)); - } + MarginLeft = 0; } } - /// - /// Number of copies to print. - /// - public int Copies + partial void OnMarginRightChanged(double value) { - get => _copies; - set + if (value < 0) { - if (_copies != value && value > 0) - { - _copies = value; - OnPropertyChanged(nameof(Copies)); - } + MarginRight = 0; } } - - /// - /// Top margin in inches. - /// - public double MarginTop - { - get => _marginTop; - set - { - if (_marginTop != value && value >= 0) - { - _marginTop = value; - OnPropertyChanged(nameof(MarginTop)); - } - } - } - - /// - /// Bottom margin in inches. - /// - public double MarginBottom - { - get => _marginBottom; - set - { - if (_marginBottom != value && value >= 0) - { - _marginBottom = value; - OnPropertyChanged(nameof(MarginBottom)); - } - } - } - - /// - /// Left margin in inches. - /// - public double MarginLeft - { - get => _marginLeft; - set - { - if (_marginLeft != value && value >= 0) - { - _marginLeft = value; - OnPropertyChanged(nameof(MarginLeft)); - } - } - } - - /// - /// Right margin in inches. - /// - public double MarginRight - { - get => _marginRight; - set - { - if (_marginRight != value && value >= 0) - { - _marginRight = value; - OnPropertyChanged(nameof(MarginRight)); - } - } - } - - /// - /// Whether to print background colors and images. - /// - public bool ShouldPrintBackgrounds - { - get => _shouldPrintBackgrounds; - set - { - if (_shouldPrintBackgrounds != value) - { - _shouldPrintBackgrounds = value; - OnPropertyChanged(nameof(ShouldPrintBackgrounds)); - } - } - } - - /// - /// Whether to print only the selected content. - /// - public bool ShouldPrintSelectionOnly - { - get => _shouldPrintSelectionOnly; - set - { - if (_shouldPrintSelectionOnly != value) - { - _shouldPrintSelectionOnly = value; - OnPropertyChanged(nameof(ShouldPrintSelectionOnly)); - } - } - } - - /// - /// Whether to print header and footer. - /// - public bool ShouldPrintHeaderAndFooter - { - get => _shouldPrintHeaderAndFooter; - set - { - if (_shouldPrintHeaderAndFooter != value) - { - _shouldPrintHeaderAndFooter = value; - OnPropertyChanged(nameof(ShouldPrintHeaderAndFooter)); - } - } - } - - /// - /// Title to display in the header. - /// - public string HeaderTitle - { - get => _headerTitle; - set - { - if (_headerTitle != value) - { - _headerTitle = value ?? string.Empty; - OnPropertyChanged(nameof(HeaderTitle)); - } - } - } - - /// - /// URI to display in the footer. - /// - public string FooterUri - { - get => _footerUri; - set - { - if (_footerUri != value) - { - _footerUri = value ?? string.Empty; - OnPropertyChanged(nameof(FooterUri)); - } - } - } - - /// - /// Scale factor for printing (0.1 to 2.0). - /// - public double ScaleFactor - { - get => _scaleFactor; - set - { - if (_scaleFactor != value && value >= 0.1 && value <= 2.0) - { - _scaleFactor = value; - OnPropertyChanged(nameof(ScaleFactor)); - } - } - } - - /// - /// Number of pages to print per sheet (1, 2, 4, 6, 9, 16). - /// - public int PagesPerSide - { - get => _pagesPerSide; - set - { - var validValues = new[] { 1, 2, 4, 6, 9, 16 }; - if (_pagesPerSide != value && System.Array.IndexOf(validValues, value) >= 0) - { - _pagesPerSide = value; - OnPropertyChanged(nameof(PagesPerSide)); - } - } - } - - /// - /// Page ranges to print (e.g., "1-3,5,7-9"). - /// - public string PageRanges - { - get => _pageRanges; - set - { - if (_pageRanges != value) - { - _pageRanges = value ?? string.Empty; - OnPropertyChanged(nameof(PageRanges)); - } - } - } - - - - protected virtual void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } -} \ No newline at end of file +} diff --git a/Wino.Core.ViewModels/CalendarBaseViewModel.cs b/Wino.Core.ViewModels/CalendarBaseViewModel.cs index 69b42bd1..8b609872 100644 --- a/Wino.Core.ViewModels/CalendarBaseViewModel.cs +++ b/Wino.Core.ViewModels/CalendarBaseViewModel.cs @@ -9,4 +9,18 @@ public class CalendarBaseViewModel : CoreBaseViewModel, IRecipient OnCalendarItemAdded(message.CalendarItem); protected virtual void OnCalendarItemAdded(CalendarItem calendarItem) { } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + Messenger.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + Messenger.Unregister(this); + } } diff --git a/Wino.Core.ViewModels/CoreBaseViewModel.cs b/Wino.Core.ViewModels/CoreBaseViewModel.cs index 92b58856..764b4235 100644 --- a/Wino.Core.ViewModels/CoreBaseViewModel.cs +++ b/Wino.Core.ViewModels/CoreBaseViewModel.cs @@ -33,9 +33,15 @@ public class CoreBaseViewModel : ObservableRecipient, } } - public virtual void OnNavigatedTo(NavigationMode mode, object parameters) { IsActive = true; } + public virtual void OnNavigatedTo(NavigationMode mode, object parameters) + { + RegisterRecipients(); + } - public virtual void OnNavigatedFrom(NavigationMode mode, object parameters) { IsActive = false; } + public virtual void OnNavigatedFrom(NavigationMode mode, object parameters) + { + UnregisterRecipients(); + } public virtual void OnPageLoaded() { } @@ -44,6 +50,16 @@ public class CoreBaseViewModel : ObservableRecipient, protected virtual void OnDispatcherAssigned() { } + /// + /// Register message recipients for this view model. Override to register specific message types. + /// + protected virtual void RegisterRecipients() { } + + /// + /// Unregister message recipients for this view model. Override to unregister specific message types. + /// + protected virtual void UnregisterRecipients() { } + protected virtual void OnAccountCreated(MailAccount createdAccount) { } protected virtual void OnAccountRemoved(MailAccount removedAccount) { } protected virtual void OnAccountUpdated(MailAccount updatedAccount) { } diff --git a/Wino.Core.WinUI/BasePage.cs b/Wino.Core.WinUI/BasePage.cs index 74b88d62..d9b36395 100644 --- a/Wino.Core.WinUI/BasePage.cs +++ b/Wino.Core.WinUI/BasePage.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using CommunityToolkit.Mvvm.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.UI.Xaml; @@ -27,6 +26,16 @@ public partial class BasePage : Page, IRecipient } public virtual void OnLanguageChanged() { } + + /// + /// Register message recipients for this page. Override to register specific message types. + /// + protected virtual void RegisterRecipients() { } + + /// + /// Unregister message recipients for this page. Override to unregister specific message types. + /// + protected virtual void UnregisterRecipients() { } } public abstract class BasePage : BasePage where T : CoreBaseViewModel @@ -49,10 +58,7 @@ public abstract class BasePage : BasePage where T : CoreBaseViewModel private void PageLoaded(object sender, RoutedEventArgs e) => ViewModel.OnPageLoaded(); - ~BasePage() - { - Debug.WriteLine($"Disposed {GetType().Name}"); - } + ~BasePage() { Debug.WriteLine($"Disposed {GetType().Name}"); } protected override void OnNavigatedTo(NavigationEventArgs e) { @@ -61,8 +67,8 @@ public abstract class BasePage : BasePage where T : CoreBaseViewModel var mode = GetNavigationMode(e.NavigationMode); var parameter = e.Parameter; - WeakReferenceMessenger.Default.UnregisterAll(this); WeakReferenceMessenger.Default.Register(this); + RegisterRecipients(); ViewModel.OnNavigatedTo(mode, parameter); } @@ -74,7 +80,8 @@ public abstract class BasePage : BasePage where T : CoreBaseViewModel var mode = GetNavigationMode(e.NavigationMode); var parameter = e.Parameter; - WeakReferenceMessenger.Default.UnregisterAll(this); + WeakReferenceMessenger.Default.Unregister(this); + UnregisterRecipients(); ViewModel.OnNavigatedFrom(mode, parameter); diff --git a/Wino.Core.WinUI/Dialogs/PrintDialog.xaml b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml index 33aa5476..11b878db 100644 --- a/Wino.Core.WinUI/Dialogs/PrintDialog.xaml +++ b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml @@ -1,220 +1,54 @@ - + - - - - - - - - + - - - - - - - + + - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - - \ No newline at end of file + + diff --git a/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs index 3680aba3..888668a4 100644 --- a/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs +++ b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs @@ -1,7 +1,11 @@ using System.Collections.Generic; +using System.Drawing.Printing; +using System.Linq; +using System.Threading.Tasks; using Microsoft.UI.Xaml.Controls; +using Serilog; +using Wino.Core.Domain.Enums; using Wino.Core.Domain.Models.Printing; -using Wino.Core.WinUI.Models; namespace Wino.Core.WinUI.Dialogs; @@ -10,32 +14,40 @@ namespace Wino.Core.WinUI.Dialogs; /// public sealed partial class PrintDialog : ContentDialog { - /// - /// The ViewModel that handles the dialog's data binding and logic. - /// - public PrintDialogViewModel ViewModel { get; } - - /// - /// Gets the configured print settings from the dialog. - /// - public WebView2PrintSettingsModel PrintSettings => ViewModel.PrintSettings; + public WebView2PrintSettingsModel PrintSettings { get; set; } = new WebView2PrintSettingsModel(); public PrintDialog() { this.InitializeComponent(); - ViewModel = new PrintDialogViewModel(); - ViewModel.Initialize(); } /// /// Initializes the dialog with existing print settings. /// /// The initial print settings to load. - public PrintDialog(WebView2PrintSettingsModel printSettings) + public PrintDialog(WebView2PrintSettingsModel printSettings = null) { + if (printSettings != null) PrintSettings = printSettings; + this.InitializeComponent(); - ViewModel = new PrintDialogViewModel(); - ViewModel.Initialize(printSettings); + } + + private void PrintDialog_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) => LoadSettingsToUI(PrintSettings); + + private void OrientationRadio_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is RadioButtons radioButtons) + { + PrintSettings.Orientation = (PrintOrientation)radioButtons.SelectedIndex; + } + } + + private void PrinterComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (sender is ComboBox comboBox && comboBox.SelectedItem != null) + { + PrintSettings.PrinterName = comboBox.SelectedItem.ToString(); + } } /// @@ -44,7 +56,85 @@ public sealed partial class PrintDialog : ContentDialog /// List of available printer names. public void SetAvailablePrinters(IEnumerable printers) { - ViewModel.SetAvailablePrinters(printers); + var printerList = printers?.ToList() ?? new List(); + + if (this.FindName("PrinterComboBox") is ComboBox printerComboBox) + { + printerComboBox.ItemsSource = printerList; + + if (printerList.Any()) + { + // Set to first printer or to the one in settings + var targetPrinter = !string.IsNullOrEmpty(PrintSettings.PrinterName) + ? PrintSettings.PrinterName + : printerList.First(); + + var index = printerList.IndexOf(targetPrinter); + printerComboBox.SelectedIndex = index >= 0 ? index : 0; + + // Update the settings model with the selected printer + PrintSettings.PrinterName = printerComboBox.SelectedItem?.ToString() ?? string.Empty; + } + } + } + + /// + /// Loads available printers asynchronously and sets them in the dialog. + /// + public async Task LoadAvailablePrintersAsync() + { + try + { + var printers = await Task.Run(() => + { + var printerList = new List(); + + // Get all installed printers using System.Drawing.Printing + foreach (string printerName in PrinterSettings.InstalledPrinters) + { + printerList.Add(printerName); + } + + return printerList.AsEnumerable(); + }); + + SetAvailablePrinters(printers); + } + catch (System.Exception ex) + { + // Log the exception if logging is available + Log.Error(ex, "Error getting available printers"); + + // Set empty list if printer discovery fails + SetAvailablePrinters(Enumerable.Empty()); + } + } + + private void LoadSettingsToUI(WebView2PrintSettingsModel settings) + { + if (settings == null) return; + + // Only handle orientation manually since other properties are bound via x:Bind + if (this.FindName("OrientationRadioButtons") is RadioButtons orientationRadio) + { + orientationRadio.SelectedIndex = (int)settings.Orientation; + } + } + + private void UpdateSettingsFromUI() + { + // Most properties are bound via x:Bind, only handle orientation manually + if (this.FindName("OrientationRadioButtons") is RadioButtons orientationRadio) + { + PrintSettings.Orientation = (PrintOrientation)orientationRadio.SelectedIndex; + } + + // Also update printer name from ComboBox since it uses ItemsSource binding + if (this.FindName("PrinterComboBox") is ComboBox printerComboBox && + printerComboBox.SelectedItem != null) + { + PrintSettings.PrinterName = printerComboBox.SelectedItem.ToString(); + } } /// @@ -53,54 +143,31 @@ public sealed partial class PrintDialog : ContentDialog /// True if settings are valid, false otherwise. private bool ValidateSettings() { - // Validate printer selection - if (string.IsNullOrWhiteSpace(PrintSettings.PrinterName)) + // Check if a printer is selected + if (this.FindName("PrinterComboBox") is ComboBox printerComboBox && + printerComboBox.SelectedItem == null) { - // Show error message or set focus to printer selection return false; } - // Validate copies + // Copies validation is handled by the bound property with validation in the model if (PrintSettings.Copies <= 0) { return false; } - // Validate page ranges if custom range is specified - if (ViewModel.IsCustomPageRange && !string.IsNullOrWhiteSpace(PrintSettings.PageRanges)) - { - // Basic validation for page ranges format - // More comprehensive validation could be added here - var pageRanges = PrintSettings.PageRanges.Trim(); - if (string.IsNullOrEmpty(pageRanges)) - { - return false; - } - } - - // Validate margins - if (PrintSettings.MarginTop < 0 || PrintSettings.MarginBottom < 0 || - PrintSettings.MarginLeft < 0 || PrintSettings.MarginRight < 0) - { - return false; - } - - // Validate scale factor - if (PrintSettings.ScaleFactor < 0.1 || PrintSettings.ScaleFactor > 2.0) - { - return false; - } - return true; } private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) { + // Update settings from UI before validation + UpdateSettingsFromUI(); + // Validate settings before closing if (!ValidateSettings()) { args.Cancel = true; - // Could show error message here } } @@ -108,4 +175,4 @@ public sealed partial class PrintDialog : ContentDialog { // Cancel was clicked, no validation needed } -} \ No newline at end of file +} diff --git a/Wino.Core.WinUI/Helpers/XamlHelpers.cs b/Wino.Core.WinUI/Helpers/XamlHelpers.cs index 3bb6f8d0..fbcf09f8 100644 --- a/Wino.Core.WinUI/Helpers/XamlHelpers.cs +++ b/Wino.Core.WinUI/Helpers/XamlHelpers.cs @@ -41,7 +41,7 @@ public static class XamlHelpers public static Visibility ReverseBoolToVisibilityConverter(bool value) => value ? Visibility.Collapsed : Visibility.Visible; public static Visibility ReverseVisibilityConverter(Visibility visibility) => visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; public static bool ReverseBoolConverter(bool value) => !value; - public static bool ShouldDisplayPreview(string text) => text.Any(x => char.IsLetter(x)); + public static bool ShouldDisplayPreview(string text) => text == null ? false : text.Any(x => char.IsLetter(x)); public static bool CountToBooleanConverter(int value) => value > 0; public static bool ObjectEquals(object obj1, object obj2) => object.Equals(obj1, obj2); public static Visibility CountToVisibilityConverter(int value) => value > 0 ? Visibility.Visible : Visibility.Collapsed; diff --git a/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs b/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs deleted file mode 100644 index 56c920a4..00000000 --- a/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Wino.Core.Domain.Models.Printing; - -namespace Wino.Core.WinUI.Interfaces; - -/// -/// Service interface for displaying the custom print dialog and managing print settings. -/// -public interface IPrintDialogService -{ - /// - /// Shows the print dialog and returns the configured print settings. - /// - /// Initial print settings to populate the dialog with. If null, default settings will be used. - /// List of available printers to show in the dialog. If null or empty, the service should attempt to discover printers. - /// - /// A task that resolves to the configured WebView2PrintSettingsModel if the user clicked Print, - /// or null if the user cancelled the dialog. - /// - Task ShowPrintDialogAsync( - WebView2PrintSettingsModel initialSettings = null, - IEnumerable availablePrinters = null); - - /// - /// Gets the list of available printers on the system. - /// - /// A task that resolves to a list of available printer names. - Task> GetAvailablePrintersAsync(); -} \ No newline at end of file diff --git a/Wino.Core.WinUI/Services/DialogServiceBase.cs b/Wino.Core.WinUI/Services/DialogServiceBase.cs index 8d90f1f4..ff834bb7 100644 --- a/Wino.Core.WinUI/Services/DialogServiceBase.cs +++ b/Wino.Core.WinUI/Services/DialogServiceBase.cs @@ -14,6 +14,7 @@ using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.Accounts; using Wino.Core.Domain.Models.Common; +using Wino.Core.Domain.Models.Printing; using Wino.Core.WinUI.Dialogs; using Wino.Core.WinUI.Extensions; using Wino.Dialogs; @@ -294,4 +295,38 @@ public class DialogServiceBase : IDialogServiceBase return dialog.Result; } + + public async Task ShowPrintDialogAsync(WebView2PrintSettingsModel initialSettings = null) + { + try + { + // Create the print dialog + var dialog = initialSettings != null + ? new PrintDialog(initialSettings) + : new PrintDialog(); + + // Set the XamlRoot for proper display + dialog.XamlRoot = GetXamlRoot(); + + // Get available printers asynchronously when the dialog is loaded + dialog.Loaded += async (sender, e) => + { + await dialog.LoadAvailablePrintersAsync(); + }; + + // Show the dialog + var result = await HandleDialogPresentationAsync(dialog); + + // Return the settings if user clicked Print, otherwise null + return result == ContentDialogResult.Primary + ? dialog.PrintSettings + : null; + } + catch (Exception ex) + { + // Log the exception if logging is available + Log.Error(ex, "Error showing print dialog"); + return null; + } + } } diff --git a/Wino.Core.WinUI/Services/PrintDialogService.cs b/Wino.Core.WinUI/Services/PrintDialogService.cs deleted file mode 100644 index 3fe5e548..00000000 --- a/Wino.Core.WinUI/Services/PrintDialogService.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Drawing.Printing; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.UI.Xaml; -using Wino.Core.Domain.Models.Printing; -using Wino.Core.WinUI.Dialogs; -using Wino.Core.WinUI.Interfaces; - -namespace Wino.Core.WinUI.Services; - -/// -/// Service implementation for displaying the custom print dialog and managing print settings. -/// -public class PrintDialogService : IPrintDialogService -{ - /// - /// Shows the print dialog and returns the configured print settings. - /// - /// Initial print settings to populate the dialog with. If null, default settings will be used. - /// List of available printers to show in the dialog. If null or empty, the service will discover printers. - /// - /// A task that resolves to the configured WebView2PrintSettingsModel if the user clicked Print, - /// or null if the user cancelled the dialog. - /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern", - Justification = "GetProperty is used for backward compatibility and gracefully handles failures")] - public async Task ShowPrintDialogAsync( - WebView2PrintSettingsModel initialSettings = null, - IEnumerable availablePrinters = null) - { - try - { - // Note: XamlRoot will be set by the calling code when showing the dialog - - // Create the print dialog - var dialog = initialSettings != null - ? new PrintDialog(initialSettings) - : new PrintDialog(); - - // The XamlRoot will be set by the calling code when showing the dialog - - // Get available printers if not provided - var printers = availablePrinters ?? await GetAvailablePrintersAsync(); - dialog.SetAvailablePrinters(printers); - - // Show the dialog - var result = await dialog.ShowAsync(); - - // Return the settings if user clicked Print, otherwise null - return result == Microsoft.UI.Xaml.Controls.ContentDialogResult.Primary - ? dialog.PrintSettings - : null; - } - catch (Exception ex) - { - // Log the exception if logging is available - System.Diagnostics.Debug.WriteLine($"Error showing print dialog: {ex.Message}"); - return null; - } - } - - /// - /// Gets the list of available printers on the system. - /// - /// A task that resolves to a list of available printer names. - public async Task> GetAvailablePrintersAsync() - { - return await Task.Run(() => - { - try - { - var printers = new List(); - - // Get all installed printers using System.Drawing.Printing - foreach (string printerName in PrinterSettings.InstalledPrinters) - { - printers.Add(printerName); - } - - return printers.AsEnumerable(); - } - catch (Exception ex) - { - // Log the exception if logging is available - System.Diagnostics.Debug.WriteLine($"Error getting available printers: {ex.Message}"); - return Enumerable.Empty(); - } - }); - } -} \ No newline at end of file diff --git a/Wino.Core.WinUI/Views/Abstract/SettingsPageBase.cs b/Wino.Core.WinUI/Views/Abstract/SettingsPageBase.cs index 7ad841da..bee7377f 100644 --- a/Wino.Core.WinUI/Views/Abstract/SettingsPageBase.cs +++ b/Wino.Core.WinUI/Views/Abstract/SettingsPageBase.cs @@ -1,6 +1,6 @@ using Microsoft.UI.Xaml; -using Wino.Core.WinUI; using Wino.Core.ViewModels; +using Wino.Core.WinUI; namespace Wino.Views.Abstract; diff --git a/Wino.Core.WinUI/Views/SettingsPage.xaml.cs b/Wino.Core.WinUI/Views/SettingsPage.xaml.cs index e498a02a..e44bfbcc 100644 --- a/Wino.Core.WinUI/Views/SettingsPage.xaml.cs +++ b/Wino.Core.WinUI/Views/SettingsPage.xaml.cs @@ -26,9 +26,6 @@ public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient(this); - SettingsFrame.Navigate(typeof(SettingOptionsPage), null, new SuppressNavigationTransitionInfo()); var initialRequest = new BreadcrumbNavigationRequested(Translator.MenuSettings, WinoPage.SettingOptionsPage); @@ -63,12 +60,23 @@ public sealed partial class SettingsPage : SettingsPageAbstract, IRecipient(this); - base.OnNavigatingFrom(e); } + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + WeakReferenceMessenger.Default.Unregister(this); + } + void IRecipient.Receive(BreadcrumbNavigationRequested message) { var pageType = ViewModel.NavigationService.GetPageType(message.PageType); diff --git a/Wino.Mail.ViewModels/AppShellViewModel.cs b/Wino.Mail.ViewModels/AppShellViewModel.cs index ece0d4b9..6862b4ba 100644 --- a/Wino.Mail.ViewModels/AppShellViewModel.cs +++ b/Wino.Mail.ViewModels/AppShellViewModel.cs @@ -1037,4 +1037,36 @@ public partial class AppShellViewModel : MailBaseViewModel, { await MenuItemInvokedOrSelectedAsync(SettingsItem, WinoPage.AppPreferencesPage); } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + } } diff --git a/Wino.Mail.ViewModels/MailBaseViewModel.cs b/Wino.Mail.ViewModels/MailBaseViewModel.cs index 33493cb8..75887487 100644 --- a/Wino.Mail.ViewModels/MailBaseViewModel.cs +++ b/Wino.Mail.ViewModels/MailBaseViewModel.cs @@ -39,4 +39,34 @@ public class MailBaseViewModel : CoreBaseViewModel, void IRecipient.Receive(FolderRenamed message) => OnFolderRenamed(message.MailItemFolder); void IRecipient.Receive(FolderSynchronizationEnabled message) => OnFolderSynchronizationEnabled(message.MailItemFolder); + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + } } diff --git a/Wino.Mail.ViewModels/MailListPageViewModel.cs b/Wino.Mail.ViewModels/MailListPageViewModel.cs index 7b2c7cf9..b0d0cbfd 100644 --- a/Wino.Mail.ViewModels/MailListPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailListPageViewModel.cs @@ -1081,4 +1081,34 @@ public partial class MailListPageViewModel : MailBaseViewModel, { // MailCollection.UpdateThumbnails(message.Email); } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + Messenger.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + Messenger.Unregister(this); + } } diff --git a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs index 4aec1f3d..e832c87c 100644 --- a/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs +++ b/Wino.Mail.ViewModels/MailRenderingPageViewModel.cs @@ -20,6 +20,7 @@ using Wino.Core.Domain.Interfaces; using Wino.Core.Domain.Models.MailItem; using Wino.Core.Domain.Models.Menus; using Wino.Core.Domain.Models.Navigation; +using Wino.Core.Domain.Models.Printing; using Wino.Core.Domain.Models.Reader; using Wino.Core.Services; using Wino.Mail.ViewModels.Data; @@ -55,7 +56,7 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, // Used in 'Save as' and 'Print' functionality. public Func> SaveHTMLasPDFFunc { get; set; } - public Action ShowPrintUIAction { get; set; } + public Func> DirectPrintFuncAsync { get; set; } #region Properties @@ -257,7 +258,22 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, } else if (operation == MailOperation.Print) { - ShowPrintUI(); + var settings = await _dialogService.ShowPrintDialogAsync(); + + if (settings == null) return; + + var printingResult = await DirectPrintFuncAsync.Invoke(settings); + + // TODO: More detailed printing result handling. + if (printingResult == PrintingResult.Submitted) + { + _dialogService.InfoBarMessage(Translator.DialogMessage_PrintingSuccessTitle, Translator.DialogMessage_PrintingSuccessMessage, InfoBarMessageType.Success); + } + else if (printingResult == PrintingResult.Failed) + { + _dialogService.InfoBarMessage(Translator.DialogMessage_PrintingFailedTitle, Translator.DialogMessage_PrintingFailedMessage, InfoBarMessageType.Error); + } + } else if (operation == MailOperation.ViewMessageSource) { @@ -682,19 +698,6 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, } } - private void ShowPrintUI() - { - try - { - ShowPrintUIAction?.Invoke(); - } - catch (Exception exception) - { - Log.Error(exception, "Failed to print mail."); - _dialogService.InfoBarMessage(string.Empty, exception.Message, InfoBarMessageType.Error); - } - } - private async Task SaveAsAsync() { try @@ -793,4 +796,20 @@ public partial class MailRenderingPageViewModel : MailBaseViewModel, } }); } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + Messenger.Register(this); + Messenger.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + Messenger.Unregister(this); + Messenger.Unregister(this); + } } diff --git a/Wino.Mail.WinUI/App.xaml.cs b/Wino.Mail.WinUI/App.xaml.cs index 1606a57e..19ce50b5 100644 --- a/Wino.Mail.WinUI/App.xaml.cs +++ b/Wino.Mail.WinUI/App.xaml.cs @@ -15,7 +15,7 @@ namespace Wino.Mail.WinUI; public partial class App : WinoApplication, IRecipient { - private ISynchronizationManager _synchronizationManager; + private ISynchronizationManager? _synchronizationManager; public App() { @@ -23,7 +23,7 @@ public partial class App : WinoApplication, IRecipient(this); + RegisterRecipients(); } #region Dependency Injection @@ -114,8 +114,13 @@ public partial class App : WinoApplication, IRecipient(this); + } + public void Receive(NewMailSynchronizationRequested message) { - _synchronizationManager.SynchronizeMailAsync(message.Options); + _synchronizationManager?.SynchronizeMailAsync(message.Options); } } diff --git a/Wino.Mail.WinUI/AppShell.xaml.cs b/Wino.Mail.WinUI/AppShell.xaml.cs index a2015171..f395d1c0 100644 --- a/Wino.Mail.WinUI/AppShell.xaml.cs +++ b/Wino.Mail.WinUI/AppShell.xaml.cs @@ -9,6 +9,7 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; +using Microsoft.UI.Xaml.Navigation; using Windows.Foundation; using Wino.Core.Domain; using Wino.Core.Domain.Entities.Mail; @@ -20,6 +21,7 @@ using Wino.Core.Domain.Models.Navigation; using Wino.Core.WinUI; using Wino.Core.WinUI.Controls; using Wino.Extensions; +using Wino.Mail.ViewModels.Data; using Wino.MenuFlyouts; using Wino.MenuFlyouts.Context; using Wino.Messaging.Client.Accounts; @@ -37,13 +39,27 @@ public sealed partial class AppShell : AppShellAbstract, IRecipient { [GeneratedDependencyProperty] - public partial UIElement TopShellContent { get; set; } + public partial UIElement? TopShellContent { get; set; } public AppShell() : base() { InitializeComponent(); } - public Frame GetShellFrame() => ShellFrame; + + //protected override void OnNavigatedTo(NavigationEventArgs e) + //{ + // base.OnNavigatedTo(e); + + // WeakReferenceMessenger.Default.Register(this); + // WeakReferenceMessenger.Default.Register(this); + // WeakReferenceMessenger.Default.Register(this); + // WeakReferenceMessenger.Default.Register(this); + //} + + protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) + { + base.OnNavigatingFrom(e); + } private async void ItemDroppedOnFolder(object sender, DragEventArgs e) { @@ -66,14 +82,14 @@ public sealed partial class AppShell : AppShellAbstract, foreach (var item in dragPackage.DraggingMails) { - //if (item is MailItemViewModel singleMailItemViewModel) - //{ - // mailCopies.Add(singleMailItemViewModel.MailCopy); - //} - //else if (item is ThreadMailItemViewModel threadViewModel) - //{ - // mailCopies.AddRange(threadViewModel.GetMailCopies()); - //} + if (item is MailItemViewModel singleMailItemViewModel) + { + mailCopies.Add(singleMailItemViewModel.MailCopy); + } + else if (item is ThreadMailItemViewModel threadViewModel) + { + mailCopies.AddRange(threadViewModel.ThreadEmails.Select(a => a.MailCopy)); + } } await ViewModel.PerformMoveOperationAsync(mailCopies, draggingFolder); @@ -114,7 +130,7 @@ public sealed partial class AppShell : AppShellAbstract, // Check whether the moving item's account has at least one same as the target folder's account. var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId); - if (!dragPackage.DraggingMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false; + if (!dragPackage.DraggingMails.Cast().Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id))) return false; return true; } @@ -320,4 +336,24 @@ public sealed partial class AppShell : AppShellAbstract, ShellFrame.Margin = new Thickness(0); } } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } } diff --git a/Wino.Mail.WinUI/Dialogs/NewImapSetupDialog.xaml.cs b/Wino.Mail.WinUI/Dialogs/NewImapSetupDialog.xaml.cs index 7cd2cde2..0772143f 100644 --- a/Wino.Mail.WinUI/Dialogs/NewImapSetupDialog.xaml.cs +++ b/Wino.Mail.WinUI/Dialogs/NewImapSetupDialog.xaml.cs @@ -103,9 +103,19 @@ public sealed partial class NewImapSetupDialog : ContentDialog, public void StartImapConnectionSetup(MailAccount account) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), account, new DrillInNavigationTransitionInfo()); public void StartImapConnectionSetup(AccountCreationDialogResult accountCreationDialogResult) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), accountCreationDialogResult, new DrillInNavigationTransitionInfo()); - private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this); + private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } - private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => WeakReferenceMessenger.Default.RegisterAll(this); + private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } // Don't hide the dialog unless dismiss is requested from the inner pages specifically. private void OnDialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args) => args.Cancel = !isDismissRequested; diff --git a/Wino.Mail.WinUI/ShellWindow.xaml.cs b/Wino.Mail.WinUI/ShellWindow.xaml.cs index efb8c768..99bbf187 100644 --- a/Wino.Mail.WinUI/ShellWindow.xaml.cs +++ b/Wino.Mail.WinUI/ShellWindow.xaml.cs @@ -15,7 +15,7 @@ using WinUIEx; namespace Wino.Mail.WinUI; -public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient +public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient, IRecipient { public IStatePersistanceService StatePersistanceService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception("StatePersistanceService not registered in DI container."); public IPreferencesService PreferencesService { get; } = WinoApplication.Current.Services.GetService() ?? throw new Exception("PreferencesService not registered in DI container."); @@ -23,8 +23,7 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient public ShellWindow() { - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); + RegisterRecipients(); InitializeComponent(); @@ -177,10 +176,24 @@ public sealed partial class ShellWindow : WindowEx, IWinoShellWindow, IRecipient // Clean up system tray _systemTrayService?.Dispose(); + UnregisterRecipients(); + // Close the window this.Close(); // Exit the application Application.Current.Exit(); } + + private void RegisterRecipients() + { + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } + + private void UnregisterRecipients() + { + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } } diff --git a/Wino.Mail.WinUI/Views/MailListPage.xaml.cs b/Wino.Mail.WinUI/Views/MailListPage.xaml.cs index 8c060362..04e3db0b 100644 --- a/Wino.Mail.WinUI/Views/MailListPage.xaml.cs +++ b/Wino.Mail.WinUI/Views/MailListPage.xaml.cs @@ -57,22 +57,12 @@ public sealed partial class MailListPage : MailListPageAbstract, { WeakReferenceMessenger.Default.Send(new ActiveMailFolderChangedEvent(folderNavigationArgs.BaseFolderMenuItem, folderNavigationArgs.FolderInitLoadAwaitTask)); } - - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); } protected override void OnNavigatedFrom(NavigationEventArgs e) { base.OnNavigatedFrom(e); - WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); - // Dispose all WinoListView items. // MailListView.Dispose(); @@ -455,6 +445,22 @@ public sealed partial class MailListPage : MailListPageAbstract, ViewModel.NavigationService.Navigate(WinoPage.IdlePage, null, NavigationReferenceFrame.RenderingFrame, NavigationTransitionType.DrillIn); } + protected override void RegisterRecipients() + { + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } + private void PageSizeChanged(object sender, SizeChangedEventArgs e) { ViewModel.MaxMailListLength = e.NewSize.Width - RENDERING_COLUMN_MIN_WIDTH; diff --git a/Wino.Mail.WinUI/Views/MailRenderingPage.xaml.cs b/Wino.Mail.WinUI/Views/MailRenderingPage.xaml.cs index 4cf4a470..6fe45934 100644 --- a/Wino.Mail.WinUI/Views/MailRenderingPage.xaml.cs +++ b/Wino.Mail.WinUI/Views/MailRenderingPage.xaml.cs @@ -11,7 +11,9 @@ using Microsoft.UI.Xaml.Navigation; using Microsoft.Web.WebView2.Core; using Windows.System; using Wino.Core.Domain; +using Wino.Core.Domain.Enums; using Wino.Core.Domain.Interfaces; +using Wino.Core.Domain.Models.Printing; using Wino.Core.WinUI.Extensions; using Wino.Mail.ViewModels.Data; using Wino.Mail.WinUI; @@ -43,7 +45,7 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract, Environment.SetEnvironmentVariable("WEBVIEW2_DEFAULT_BACKGROUND_COLOR", "00FFFFFF"); Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--enable-features=OverlayScrollbar,msOverlayScrollbarWinStyle,msOverlayScrollbarWinStyleAnimation,msWebView2CodeCache"); - ViewModel.ShowPrintUIAction = ShowPrintUI; + ViewModel.DirectPrintFuncAsync = DirectPrintAsync; ViewModel.SaveHTMLasPDFFunc = new Func>((path) => { @@ -51,19 +53,25 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract, }); } - private async void ShowPrintUI() + private async Task DirectPrintAsync(WebView2PrintSettingsModel settings) { - if (Chromium.CoreWebView2 == null) return; + if (Chromium.CoreWebView2 == null) return PrintingResult.Failed; - // TODO: Footer still shows wino.mail/html, there is no way to change it currently. - // TODO: ShowPrintUI - System doesn't open. - // TODO: ShowPrintUI - System doesn't open. + try + { + var nativeSettings = settings.ToCoreWebView2PrintSettings(Chromium.CoreWebView2.Environment); + var res = await Chromium.CoreWebView2.PrintAsync(nativeSettings); - // Set the document title before printing. This title will be used in the print dialog and header. - await Chromium.CoreWebView2.ExecuteScriptAsync($"document.title = '{ViewModel.Subject}';"); - var settings = Chromium.CoreWebView2.Environment.CreatePrintSettings(); - - Chromium.CoreWebView2?.ShowPrintUI(CoreWebView2PrintDialogKind.System); + return res switch + { + CoreWebView2PrintStatus.Succeeded => PrintingResult.Submitted, + _ => PrintingResult.Failed, + }; + } + catch (Exception) + { + return PrintingResult.Failed; + } } public override async void OnEditorThemeChanged() @@ -138,10 +146,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract, { base.OnNavigatedFrom(e); - WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); - WeakReferenceMessenger.Default.Unregister(this); - // Disposing the page. // Make sure the WebView2 is disposed properly. @@ -171,10 +175,6 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract, { base.OnNavigatedTo(e); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - WeakReferenceMessenger.Default.Register(this); - var anim = ConnectedAnimationService.GetForCurrentView().GetAnimation("WebViewConnectedAnimation"); anim?.TryStart(Chromium); @@ -297,4 +297,22 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract, hyperlinkButton.ContextFlyout.ShowAt(hyperlinkButton); } } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } } diff --git a/Wino.Mail/AppShell.xaml.cs b/Wino.Mail/AppShell.xaml.cs index b83f4315..822ce865 100644 --- a/Wino.Mail/AppShell.xaml.cs +++ b/Wino.Mail/AppShell.xaml.cs @@ -332,4 +332,24 @@ public sealed partial class AppShell : AppShellAbstract, ShellFrame.Margin = new Thickness(0); } } + + protected override void RegisterRecipients() + { + base.RegisterRecipients(); + + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } + + protected override void UnregisterRecipients() + { + base.UnregisterRecipients(); + + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } } diff --git a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs index e2cf46f3..ee6c8cb7 100644 --- a/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs +++ b/Wino.Mail/Dialogs/NewImapSetupDialog.xaml.cs @@ -102,9 +102,19 @@ public sealed partial class NewImapSetupDialog : ContentDialog, public void StartImapConnectionSetup(MailAccount account) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), account, new DrillInNavigationTransitionInfo()); public void StartImapConnectionSetup(AccountCreationDialogResult accountCreationDialogResult) => ImapFrame.Navigate(typeof(WelcomeImapSetupPage), accountCreationDialogResult, new DrillInNavigationTransitionInfo()); - private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) => WeakReferenceMessenger.Default.UnregisterAll(this); + private void ImapSetupDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) + { + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + WeakReferenceMessenger.Default.Unregister(this); + } - private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) => WeakReferenceMessenger.Default.RegisterAll(this); + private void ImapSetupDialogOpened(ContentDialog sender, ContentDialogOpenedEventArgs args) + { + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + } // Don't hide the dialog unless dismiss is requested from the inner pages specifically. private void OnDialogClosing(ContentDialog sender, ContentDialogClosingEventArgs args) => args.Cancel = !isDismissRequested;