using System; using System.IO; using XP.Common.Localization; using XP.Common.Logging.Interfaces; using XP.Common.PdfViewer.Exceptions; using XP.Common.PdfViewer.Interfaces; using XP.Common.PdfViewer.ViewModels; using XP.Common.PdfViewer.Views; namespace XP.Common.PdfViewer.Implementations { /// /// PDF 查看服务实现 | PDF viewer service implementation /// 基于 Telerik RadPdfViewer 实现 | Based on Telerik RadPdfViewer /// public class PdfViewerService : IPdfViewerService { private readonly ILoggerService _logger; private readonly IPdfPrintService _printService; private bool _disposed; public PdfViewerService(ILoggerService logger, IPdfPrintService printService) { _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); _printService = printService ?? throw new ArgumentNullException(nameof(printService)); } /// /// 通过文件路径打开 PDF 阅读器窗口 | Open PDF viewer window by file path /// /// PDF 文件路径 | PDF file path /// 文件不存在 | File not found /// PDF 格式无效 | Invalid PDF format public void OpenViewer(string filePath) { ObjectDisposedException.ThrowIf(_disposed, this); // 1. 验证文件路径存在 | Validate file path exists if (string.IsNullOrWhiteSpace(filePath)) { throw new ArgumentNullException(nameof(filePath), "文件路径不能为空 | File path cannot be null or empty"); } if (!File.Exists(filePath)) { _logger.Error(new FileNotFoundException(filePath), "文件不存在 | File not found: {FilePath}", filePath); throw new FileNotFoundException( $"文件不存在 | File not found: {filePath}", filePath); } try { // 2. 创建 PdfViewerWindowViewModel | Create PdfViewerWindowViewModel var viewModel = new PdfViewerWindowViewModel(filePath, _printService, _logger); // 3. 创建 PdfViewerWindow 并设置 DataContext | Create PdfViewerWindow and set DataContext var window = new PdfViewerWindow { DataContext = viewModel }; // 4. 记录 Info 日志 | Log info _logger.Info("打开 PDF 阅读器窗口 | Opening PDF viewer window: {FileName}", Path.GetFileName(filePath)); // 5. 显示窗口(非模态)| Show window (non-modal) window.Show(); } catch (Exception ex) when (ex is not FileNotFoundException and not ArgumentNullException and not PdfLoadException) { _logger.Error(ex, "PDF 文件加载失败 | PDF file load failed: {FilePath}", filePath); throw new PdfLoadException( "PDF 文件加载失败 | PDF file load failed", filePath, ex); } } /// /// 通过文件流打开 PDF 阅读器窗口 | Open PDF viewer window by stream /// /// PDF 文件流 | PDF file stream /// 窗口标题(可选)| Window title (optional) /// 流为 null | Stream is null /// PDF 格式无效 | Invalid PDF format public void OpenViewer(Stream stream, string? title = null) { ObjectDisposedException.ThrowIf(_disposed, this); // 1. 验证 stream 非 null | Validate stream is not null ArgumentNullException.ThrowIfNull(stream, nameof(stream)); try { // 2. 创建 ViewModel | Create ViewModel var viewModel = new PdfViewerWindowViewModel(stream, title, _printService, _logger); // 3. 创建 PdfViewerWindow 并设置 DataContext | Create PdfViewerWindow and set DataContext var window = new PdfViewerWindow { DataContext = viewModel }; // 4. 记录 Info 日志 | Log info var displayTitle = title ?? LocalizationHelper.Get("PdfViewer_Title"); _logger.Info("打开 PDF 阅读器窗口(流模式)| Opening PDF viewer window (stream mode): {Title}", displayTitle); // 5. 显示窗口(非模态)| Show window (non-modal) window.Show(); } catch (Exception ex) when (ex is not ArgumentNullException and not PdfLoadException) { _logger.Error(ex, "PDF 文件加载失败(流模式)| PDF file load failed (stream mode)"); throw new PdfLoadException( "PDF 文件加载失败 | PDF file load failed", null, ex); } } #region IDisposable 模式 | IDisposable Pattern /// /// 释放资源 | Dispose resources /// public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } /// /// 释放资源的核心方法 | Core dispose method /// /// 是否由 Dispose() 调用 | Whether called by Dispose() protected virtual void Dispose(bool disposing) { if (_disposed) { return; } if (disposing) { // 释放托管资源 | Dispose managed resources _logger.Info("PdfViewerService 已释放 | PdfViewerService disposed"); } _disposed = true; } /// /// 终结器安全网 | Finalizer safety net /// 确保未显式释放时仍能清理非托管资源 | Ensures unmanaged resources are cleaned up if not explicitly disposed /// ~PdfViewerService() { Dispose(disposing: false); } #endregion } }