From 4191b7314fe928ee6d9c3ad677f988b49b05d49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 20 Oct 2025 21:10:29 +0200 Subject: [PATCH] Custom print dialog. --- Wino.Core.Domain/Enums/PrintCollation.cs | 22 ++ Wino.Core.Domain/Enums/PrintColorMode.cs | 22 ++ Wino.Core.Domain/Enums/PrintDuplex.cs | 27 ++ Wino.Core.Domain/Enums/PrintMediaSize.cs | 57 +++ Wino.Core.Domain/Enums/PrintOrientation.cs | 17 + .../Printing/WebView2PrintSettingsModel.cs | 344 ++++++++++++++++++ Wino.Core.WinUI/Dialogs/PrintDialog.xaml | 220 +++++++++++ Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs | 111 ++++++ .../Extensions/PrintSettingsExtensions.cs | 212 +++++++++++ .../Interfaces/IPrintDialogService.cs | 30 ++ .../Models/PrintDialogViewModel.cs | 276 ++++++++++++++ .../Services/PrintDialogService.cs | 93 +++++ Wino.Core.WinUI/Services/PrintService.cs | 1 + 13 files changed, 1432 insertions(+) create mode 100644 Wino.Core.Domain/Enums/PrintCollation.cs create mode 100644 Wino.Core.Domain/Enums/PrintColorMode.cs create mode 100644 Wino.Core.Domain/Enums/PrintDuplex.cs create mode 100644 Wino.Core.Domain/Enums/PrintMediaSize.cs create mode 100644 Wino.Core.Domain/Enums/PrintOrientation.cs create mode 100644 Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs create mode 100644 Wino.Core.WinUI/Dialogs/PrintDialog.xaml create mode 100644 Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs create mode 100644 Wino.Core.WinUI/Extensions/PrintSettingsExtensions.cs create mode 100644 Wino.Core.WinUI/Interfaces/IPrintDialogService.cs create mode 100644 Wino.Core.WinUI/Models/PrintDialogViewModel.cs create mode 100644 Wino.Core.WinUI/Services/PrintDialogService.cs diff --git a/Wino.Core.Domain/Enums/PrintCollation.cs b/Wino.Core.Domain/Enums/PrintCollation.cs new file mode 100644 index 00000000..94b5bf93 --- /dev/null +++ b/Wino.Core.Domain/Enums/PrintCollation.cs @@ -0,0 +1,22 @@ +namespace Wino.Core.Domain.Enums; + +/// +/// Print collation options. +/// +public enum PrintCollation +{ + /// + /// Default collation. + /// + Default = 0, + + /// + /// Collated printing. + /// + Collated = 1, + + /// + /// Uncollated printing. + /// + Uncollated = 2 +} \ No newline at end of file diff --git a/Wino.Core.Domain/Enums/PrintColorMode.cs b/Wino.Core.Domain/Enums/PrintColorMode.cs new file mode 100644 index 00000000..869f9b15 --- /dev/null +++ b/Wino.Core.Domain/Enums/PrintColorMode.cs @@ -0,0 +1,22 @@ +namespace Wino.Core.Domain.Enums; + +/// +/// Print color mode options. +/// +public enum PrintColorMode +{ + /// + /// Default color mode. + /// + Default = 0, + + /// + /// Color printing. + /// + Color = 1, + + /// + /// Grayscale printing. + /// + Grayscale = 2 +} \ No newline at end of file diff --git a/Wino.Core.Domain/Enums/PrintDuplex.cs b/Wino.Core.Domain/Enums/PrintDuplex.cs new file mode 100644 index 00000000..51becc23 --- /dev/null +++ b/Wino.Core.Domain/Enums/PrintDuplex.cs @@ -0,0 +1,27 @@ +namespace Wino.Core.Domain.Enums; + +/// +/// Print duplex (double-sided) options. +/// +public enum PrintDuplex +{ + /// + /// Default duplex mode. + /// + Default = 0, + + /// + /// Single-sided printing. + /// + Simplex = 1, + + /// + /// Double-sided printing with pages flipped horizontally. + /// + DuplexShortEdge = 2, + + /// + /// Double-sided printing with pages flipped vertically. + /// + DuplexLongEdge = 3 +} \ No newline at end of file diff --git a/Wino.Core.Domain/Enums/PrintMediaSize.cs b/Wino.Core.Domain/Enums/PrintMediaSize.cs new file mode 100644 index 00000000..5e454992 --- /dev/null +++ b/Wino.Core.Domain/Enums/PrintMediaSize.cs @@ -0,0 +1,57 @@ +namespace Wino.Core.Domain.Enums; + +/// +/// Print media size options. +/// +public enum PrintMediaSize +{ + /// + /// Default media size. + /// + Default = 0, + + /// + /// Letter size (8.5 x 11 inches). + /// + NorthAmericaLetter = 1, + + /// + /// Legal size (8.5 x 14 inches). + /// + NorthAmericaLegal = 2, + + /// + /// A4 size (210 x 297 mm). + /// + IsoA4 = 3, + + /// + /// A3 size (297 x 420 mm). + /// + IsoA3 = 4, + + /// + /// A5 size (148 x 210 mm). + /// + IsoA5 = 5, + + /// + /// Tabloid size (11 x 17 inches). + /// + NorthAmericaTabloid = 6, + + /// + /// Executive size (7.25 x 10.5 inches). + /// + NorthAmericaExecutive = 7, + + /// + /// B4 size (250 x 353 mm). + /// + JisB4 = 8, + + /// + /// B5 size (176 x 250 mm). + /// + JisB5 = 9 +} \ No newline at end of file diff --git a/Wino.Core.Domain/Enums/PrintOrientation.cs b/Wino.Core.Domain/Enums/PrintOrientation.cs new file mode 100644 index 00000000..8b9b169c --- /dev/null +++ b/Wino.Core.Domain/Enums/PrintOrientation.cs @@ -0,0 +1,17 @@ +namespace Wino.Core.Domain.Enums; + +/// +/// Print orientation options. +/// +public enum PrintOrientation +{ + /// + /// Portrait orientation (default). + /// + Portrait = 0, + + /// + /// Landscape orientation. + /// + Landscape = 1 +} \ No newline at end of file diff --git a/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs b/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs new file mode 100644 index 00000000..9a2fdeaa --- /dev/null +++ b/Wino.Core.Domain/Models/Printing/WebView2PrintSettingsModel.cs @@ -0,0 +1,344 @@ +using System.ComponentModel; +using Wino.Core.Domain.Enums; + +namespace Wino.Core.Domain.Models.Printing; + +/// +/// Wrapper model for CoreWebView2PrintSettings that provides bindable properties for UI controls. +/// +public class WebView2PrintSettingsModel : INotifyPropertyChanged +{ + 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; + + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Name of the printer to use for printing. + /// + public string PrinterName + { + get => _printerName; + set + { + if (_printerName != value) + { + _printerName = value; + OnPropertyChanged(nameof(PrinterName)); + } + } + } + + /// + /// Orientation of the printed document. + /// + public PrintOrientation Orientation + { + get => _orientation; + set + { + if (_orientation != value) + { + _orientation = value; + OnPropertyChanged(nameof(Orientation)); + } + } + } + + /// + /// Color mode for printing. + /// + public PrintColorMode ColorMode + { + get => _colorMode; + set + { + if (_colorMode != value) + { + _colorMode = value; + OnPropertyChanged(nameof(ColorMode)); + } + } + } + + /// + /// Collation setting for multiple copies. + /// + public PrintCollation Collation + { + get => _collation; + set + { + if (_collation != value) + { + _collation = value; + OnPropertyChanged(nameof(Collation)); + } + } + } + + /// + /// Duplex printing mode. + /// + public PrintDuplex Duplex + { + get => _duplex; + set + { + if (_duplex != value) + { + _duplex = value; + OnPropertyChanged(nameof(Duplex)); + } + } + } + + /// + /// Media size for printing. + /// + public PrintMediaSize MediaSize + { + get => _mediaSize; + set + { + if (_mediaSize != value) + { + _mediaSize = value; + OnPropertyChanged(nameof(MediaSize)); + } + } + } + + /// + /// Number of copies to print. + /// + public int Copies + { + get => _copies; + set + { + if (_copies != value && value > 0) + { + _copies = value; + OnPropertyChanged(nameof(Copies)); + } + } + } + + /// + /// 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.WinUI/Dialogs/PrintDialog.xaml b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml new file mode 100644 index 00000000..33aa5476 --- /dev/null +++ b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs new file mode 100644 index 00000000..3680aba3 --- /dev/null +++ b/Wino.Core.WinUI/Dialogs/PrintDialog.xaml.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using Microsoft.UI.Xaml.Controls; +using Wino.Core.Domain.Models.Printing; +using Wino.Core.WinUI.Models; + +namespace Wino.Core.WinUI.Dialogs; + +/// +/// Custom print dialog for configuring WebView2 print settings. +/// +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 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) + { + this.InitializeComponent(); + ViewModel = new PrintDialogViewModel(); + ViewModel.Initialize(printSettings); + } + + /// + /// Sets the list of available printers for the dialog. + /// + /// List of available printer names. + public void SetAvailablePrinters(IEnumerable printers) + { + ViewModel.SetAvailablePrinters(printers); + } + + /// + /// Validates the current print settings before closing the dialog. + /// + /// True if settings are valid, false otherwise. + private bool ValidateSettings() + { + // Validate printer selection + if (string.IsNullOrWhiteSpace(PrintSettings.PrinterName)) + { + // Show error message or set focus to printer selection + return false; + } + + // Validate copies + 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) + { + // Validate settings before closing + if (!ValidateSettings()) + { + args.Cancel = true; + // Could show error message here + } + } + + private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + // Cancel was clicked, no validation needed + } +} \ No newline at end of file diff --git a/Wino.Core.WinUI/Extensions/PrintSettingsExtensions.cs b/Wino.Core.WinUI/Extensions/PrintSettingsExtensions.cs new file mode 100644 index 00000000..33a50ec1 --- /dev/null +++ b/Wino.Core.WinUI/Extensions/PrintSettingsExtensions.cs @@ -0,0 +1,212 @@ +using Microsoft.Web.WebView2.Core; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Printing; + +namespace Wino.Core.WinUI.Extensions; + +/// +/// Extension methods and utilities for converting between Domain print models and CoreWebView2 print settings. +/// +public static class PrintSettingsExtensions +{ + /// + /// Converts a Domain PrintOrientation to CoreWebView2PrintOrientation. + /// + public static CoreWebView2PrintOrientation ToCoreWebView2Orientation(this PrintOrientation orientation) + { + return orientation switch + { + PrintOrientation.Portrait => CoreWebView2PrintOrientation.Portrait, + PrintOrientation.Landscape => CoreWebView2PrintOrientation.Landscape, + _ => CoreWebView2PrintOrientation.Portrait + }; + } + + /// + /// Converts a CoreWebView2PrintOrientation to Domain PrintOrientation. + /// + public static PrintOrientation ToDomainOrientation(this CoreWebView2PrintOrientation orientation) + { + return orientation switch + { + CoreWebView2PrintOrientation.Portrait => PrintOrientation.Portrait, + CoreWebView2PrintOrientation.Landscape => PrintOrientation.Landscape, + _ => PrintOrientation.Portrait + }; + } + + /// + /// Converts a Domain PrintColorMode to CoreWebView2PrintColorMode. + /// + public static CoreWebView2PrintColorMode ToCoreWebView2ColorMode(this PrintColorMode colorMode) + { + return colorMode switch + { + PrintColorMode.Default => CoreWebView2PrintColorMode.Default, + PrintColorMode.Color => CoreWebView2PrintColorMode.Color, + PrintColorMode.Grayscale => CoreWebView2PrintColorMode.Grayscale, + _ => CoreWebView2PrintColorMode.Default + }; + } + + /// + /// Converts a CoreWebView2PrintColorMode to Domain PrintColorMode. + /// + public static PrintColorMode ToDomainColorMode(this CoreWebView2PrintColorMode colorMode) + { + return colorMode switch + { + CoreWebView2PrintColorMode.Default => PrintColorMode.Default, + CoreWebView2PrintColorMode.Color => PrintColorMode.Color, + CoreWebView2PrintColorMode.Grayscale => PrintColorMode.Grayscale, + _ => PrintColorMode.Default + }; + } + + /// + /// Converts a Domain PrintCollation to CoreWebView2PrintCollation. + /// + public static CoreWebView2PrintCollation ToCoreWebView2Collation(this PrintCollation collation) + { + return collation switch + { + PrintCollation.Default => CoreWebView2PrintCollation.Default, + PrintCollation.Collated => CoreWebView2PrintCollation.Collated, + PrintCollation.Uncollated => CoreWebView2PrintCollation.Uncollated, + _ => CoreWebView2PrintCollation.Default + }; + } + + /// + /// Converts a CoreWebView2PrintCollation to Domain PrintCollation. + /// + public static PrintCollation ToDomainCollation(this CoreWebView2PrintCollation collation) + { + return collation switch + { + CoreWebView2PrintCollation.Default => PrintCollation.Default, + CoreWebView2PrintCollation.Collated => PrintCollation.Collated, + CoreWebView2PrintCollation.Uncollated => PrintCollation.Uncollated, + _ => PrintCollation.Default + }; + } + + /// + /// Converts a Domain PrintDuplex to CoreWebView2PrintDuplex. + /// + public static CoreWebView2PrintDuplex ToCoreWebView2Duplex(this PrintDuplex duplex) + { + // Note: Simplified mapping due to enum value differences + return duplex switch + { + PrintDuplex.Default => CoreWebView2PrintDuplex.Default, + _ => CoreWebView2PrintDuplex.Default + }; + } + + /// + /// Converts a CoreWebView2PrintDuplex to Domain PrintDuplex. + /// + public static PrintDuplex ToDomainDuplex(this CoreWebView2PrintDuplex duplex) + { + // Note: Simplified mapping due to enum value differences + return duplex switch + { + CoreWebView2PrintDuplex.Default => PrintDuplex.Default, + _ => PrintDuplex.Default + }; + } + + /// + /// Converts a Domain PrintMediaSize to CoreWebView2PrintMediaSize. + /// + public static CoreWebView2PrintMediaSize ToCoreWebView2MediaSize(this PrintMediaSize mediaSize) + { + // Note: Simplified mapping due to enum value differences + return mediaSize switch + { + PrintMediaSize.Default => CoreWebView2PrintMediaSize.Default, + _ => CoreWebView2PrintMediaSize.Default + }; + } + + /// + /// Converts a CoreWebView2PrintMediaSize to Domain PrintMediaSize. + /// + public static PrintMediaSize ToDomainMediaSize(this CoreWebView2PrintMediaSize mediaSize) + { + // Note: Simplified mapping due to enum value differences + return mediaSize switch + { + CoreWebView2PrintMediaSize.Default => PrintMediaSize.Default, + _ => PrintMediaSize.Default + }; + } + + /// + /// Creates a CoreWebView2PrintSettings object from a WebView2PrintSettingsModel. + /// + /// The domain model containing the print settings. + /// The CoreWebView2Environment to create the settings object. + /// A configured CoreWebView2PrintSettings object. + public static CoreWebView2PrintSettings ToCoreWebView2PrintSettings( + this WebView2PrintSettingsModel model, + CoreWebView2Environment environment) + { + var settings = environment.CreatePrintSettings(); + + settings.PrinterName = model.PrinterName; + settings.Orientation = model.Orientation.ToCoreWebView2Orientation(); + settings.ColorMode = model.ColorMode.ToCoreWebView2ColorMode(); + settings.Collation = model.Collation.ToCoreWebView2Collation(); + settings.Duplex = model.Duplex.ToCoreWebView2Duplex(); + settings.MediaSize = model.MediaSize.ToCoreWebView2MediaSize(); + settings.Copies = model.Copies; + settings.MarginTop = model.MarginTop; + settings.MarginBottom = model.MarginBottom; + settings.MarginLeft = model.MarginLeft; + settings.MarginRight = model.MarginRight; + settings.ShouldPrintBackgrounds = model.ShouldPrintBackgrounds; + settings.ShouldPrintSelectionOnly = model.ShouldPrintSelectionOnly; + settings.ShouldPrintHeaderAndFooter = model.ShouldPrintHeaderAndFooter; + settings.HeaderTitle = model.HeaderTitle; + settings.FooterUri = model.FooterUri; + settings.ScaleFactor = model.ScaleFactor; + settings.PagesPerSide = model.PagesPerSide; + settings.PageRanges = model.PageRanges; + + return settings; + } + + /// + /// Updates a WebView2PrintSettingsModel from a CoreWebView2PrintSettings object. + /// + /// The domain model to update. + /// The source CoreWebView2PrintSettings. + public static void FromCoreWebView2PrintSettings( + this WebView2PrintSettingsModel model, + CoreWebView2PrintSettings settings) + { + if (settings == null) return; + + model.PrinterName = settings.PrinterName ?? string.Empty; + model.Orientation = settings.Orientation.ToDomainOrientation(); + model.ColorMode = settings.ColorMode.ToDomainColorMode(); + model.Collation = settings.Collation.ToDomainCollation(); + model.Duplex = settings.Duplex.ToDomainDuplex(); + model.MediaSize = settings.MediaSize.ToDomainMediaSize(); + model.Copies = settings.Copies; + model.MarginTop = settings.MarginTop; + model.MarginBottom = settings.MarginBottom; + model.MarginLeft = settings.MarginLeft; + model.MarginRight = settings.MarginRight; + model.ShouldPrintBackgrounds = settings.ShouldPrintBackgrounds; + model.ShouldPrintSelectionOnly = settings.ShouldPrintSelectionOnly; + model.ShouldPrintHeaderAndFooter = settings.ShouldPrintHeaderAndFooter; + model.HeaderTitle = settings.HeaderTitle ?? string.Empty; + model.FooterUri = settings.FooterUri ?? string.Empty; + model.ScaleFactor = settings.ScaleFactor; + model.PagesPerSide = settings.PagesPerSide; + model.PageRanges = settings.PageRanges ?? string.Empty; + } +} \ No newline at end of file diff --git a/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs b/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs new file mode 100644 index 00000000..56c920a4 --- /dev/null +++ b/Wino.Core.WinUI/Interfaces/IPrintDialogService.cs @@ -0,0 +1,30 @@ +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/Models/PrintDialogViewModel.cs b/Wino.Core.WinUI/Models/PrintDialogViewModel.cs new file mode 100644 index 00000000..7947403e --- /dev/null +++ b/Wino.Core.WinUI/Models/PrintDialogViewModel.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Microsoft.UI.Xaml.Controls; +using Wino.Core.Domain.Enums; +using Wino.Core.Domain.Models.Printing; + +namespace Wino.Core.WinUI.Dialogs; + +/// +/// ViewModel for the PrintDialog that handles data binding and state management. +/// +public class PrintDialogViewModel : INotifyPropertyChanged +{ + private List _availablePrinters = new(); + private bool _isCustomPageRange = false; + private WebView2PrintSettingsModel _printSettings = new(); + + public event PropertyChangedEventHandler PropertyChanged; + + public PrintDialogViewModel() + { + // Initialize default values + PrintSettings.PropertyChanged += OnPrintSettingsChanged; + } + + /// + /// The print settings model that will be configured by the dialog. + /// + public WebView2PrintSettingsModel PrintSettings + { + get => _printSettings; + set + { + if (_printSettings != value) + { + if (_printSettings != null) + _printSettings.PropertyChanged -= OnPrintSettingsChanged; + + _printSettings = value; + + if (_printSettings != null) + _printSettings.PropertyChanged += OnPrintSettingsChanged; + + OnPropertyChanged(nameof(PrintSettings)); + UpdateDerivedProperties(); + } + } + } + + /// + /// List of available printers. + /// + public List AvailablePrinters + { + get => _availablePrinters; + set + { + if (_availablePrinters != value) + { + _availablePrinters = value ?? new List(); + OnPropertyChanged(nameof(AvailablePrinters)); + } + } + } + + /// + /// Index for the orientation radio buttons. + /// + public int OrientationIndex + { + get => (int)PrintSettings.Orientation; + set + { + if (value >= 0 && value <= 1) + { + PrintSettings.Orientation = (PrintOrientation)value; + OnPropertyChanged(nameof(OrientationIndex)); + } + } + } + + /// + /// Index for the color mode radio buttons. + /// + public int ColorModeIndex + { + get => (int)PrintSettings.ColorMode; + set + { + if (value >= 0 && value <= 2) + { + PrintSettings.ColorMode = (PrintColorMode)value; + OnPropertyChanged(nameof(ColorModeIndex)); + } + } + } + + /// + /// Index for the collation radio buttons. + /// + public int CollationIndex + { + get => (int)PrintSettings.Collation; + set + { + if (value >= 0 && value <= 2) + { + PrintSettings.Collation = (PrintCollation)value; + OnPropertyChanged(nameof(CollationIndex)); + } + } + } + + /// + /// Index for the duplex radio buttons. + /// + public int DuplexIndex + { + get => (int)PrintSettings.Duplex; + set + { + if (value >= 0 && value <= 3) + { + PrintSettings.Duplex = (PrintDuplex)value; + OnPropertyChanged(nameof(DuplexIndex)); + } + } + } + + /// + /// Index for the media size combo box. + /// + public int MediaSizeIndex + { + get => (int)PrintSettings.MediaSize; + set + { + if (value >= 0 && value <= 9) + { + PrintSettings.MediaSize = (PrintMediaSize)value; + OnPropertyChanged(nameof(MediaSizeIndex)); + } + } + } + + /// + /// Index for the pages per side combo box. + /// + public int PagesPerSideIndex + { + get + { + var validValues = new[] { 1, 2, 4, 6, 9, 16 }; + return Array.IndexOf(validValues, PrintSettings.PagesPerSide); + } + set + { + var validValues = new[] { 1, 2, 4, 6, 9, 16 }; + if (value >= 0 && value < validValues.Length) + { + PrintSettings.PagesPerSide = validValues[value]; + OnPropertyChanged(nameof(PagesPerSideIndex)); + } + } + } + + /// + /// Index for the page range option (0 = All pages, 1 = Custom range). + /// + public int PageRangeOptionIndex + { + get => IsCustomPageRange ? 1 : 0; + set + { + IsCustomPageRange = value == 1; + if (!IsCustomPageRange) + { + PrintSettings.PageRanges = string.Empty; + } + OnPropertyChanged(nameof(PageRangeOptionIndex)); + } + } + + /// + /// Whether custom page range is selected. + /// + public bool IsCustomPageRange + { + get => _isCustomPageRange; + private set + { + if (_isCustomPageRange != value) + { + _isCustomPageRange = value; + OnPropertyChanged(nameof(IsCustomPageRange)); + } + } + } + + /// + /// Scale factor as percentage text for display. + /// + public string ScalePercentageText => $"{(int)(PrintSettings.ScaleFactor * 100)}%"; + + /// + /// Initializes the dialog with the provided print settings. + /// + /// The initial print settings. + public void Initialize(WebView2PrintSettingsModel printSettings = null) + { + if (printSettings != null) + { + PrintSettings = printSettings; + } + else + { + PrintSettings = new WebView2PrintSettingsModel(); + } + + UpdateDerivedProperties(); + } + + /// + /// Sets the list of available printers. + /// + /// List of printer names. + public void SetAvailablePrinters(IEnumerable printers) + { + AvailablePrinters = printers?.ToList() ?? new List(); + + // If current printer is not in the list, select the first one + if (AvailablePrinters.Any() && !AvailablePrinters.Contains(PrintSettings.PrinterName)) + { + PrintSettings.PrinterName = AvailablePrinters.First(); + } + } + + private void OnPrintSettingsChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(WebView2PrintSettingsModel.ScaleFactor)) + { + OnPropertyChanged(nameof(ScalePercentageText)); + } + else if (e.PropertyName == nameof(WebView2PrintSettingsModel.PageRanges)) + { + // Update custom page range flag based on whether page ranges is empty + if (!string.IsNullOrWhiteSpace(PrintSettings.PageRanges)) + { + IsCustomPageRange = true; + OnPropertyChanged(nameof(PageRangeOptionIndex)); + } + } + } + + private void UpdateDerivedProperties() + { + OnPropertyChanged(nameof(OrientationIndex)); + OnPropertyChanged(nameof(ColorModeIndex)); + OnPropertyChanged(nameof(CollationIndex)); + OnPropertyChanged(nameof(DuplexIndex)); + OnPropertyChanged(nameof(MediaSizeIndex)); + OnPropertyChanged(nameof(PagesPerSideIndex)); + OnPropertyChanged(nameof(PageRangeOptionIndex)); + OnPropertyChanged(nameof(ScalePercentageText)); + + // Update custom page range based on current page ranges value + IsCustomPageRange = !string.IsNullOrWhiteSpace(PrintSettings.PageRanges); + } + + protected virtual void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} \ No newline at end of file diff --git a/Wino.Core.WinUI/Services/PrintDialogService.cs b/Wino.Core.WinUI/Services/PrintDialogService.cs new file mode 100644 index 00000000..3fe5e548 --- /dev/null +++ b/Wino.Core.WinUI/Services/PrintDialogService.cs @@ -0,0 +1,93 @@ +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/Services/PrintService.cs b/Wino.Core.WinUI/Services/PrintService.cs index 92add32d..f55a9916 100644 --- a/Wino.Core.WinUI/Services/PrintService.cs +++ b/Wino.Core.WinUI/Services/PrintService.cs @@ -21,6 +21,7 @@ namespace Wino.Core.WinUI.Services; /// HTML file is saved as PDF to temporary location. /// Then PDF is loaded as PdfDocument and printed using CanvasBitmap for each page. /// + public class PrintService : IPrintService { private TaskCompletionSource _taskCompletionSource;