当前位置:首页 > 工具 > 正文内容

WPF .NET Core环境下实现PDF预览的两种技术方案

访客 工具 2026年9月28日 13

方案一:集成 WebView2 控件

该方法依托微软官方提供的 WebView2 组件,通过嵌入 Chromium 内核实现对 PDF 文件的原生渲染。此方案优势在于渲染质量高且支持现代 PDF 特性,但依赖终端环境安装 WebView2 运行时。

首先在项目依赖管理中检索并安装 Microsoft.Web.WebView2 程序包。安装完成后,在视图层注册控件命名空间并构建基础布局:

<UserControl x:Class="AppCore.Views.DocumentPreviewPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf">
    <Grid Background="#F8F9FA">
        <Grid.RowDefinitions>
            <RowDefinition Height="48"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <Button x:Name="NavigationBackBtn" Grid.Row="0" 
                Content="返回" HorizontalAlignment="Left" Margin="12" 
                Click="ExecuteNavigationBack"/>
        
        <wv2:WebView2 x:Name="EmbeddedBrowserControl" Grid.Row="1" 
                      Loaded="OnBrowserEngineInitialized"/>
    </Grid>
</UserControl>

后端逻辑主要负责异步初始化浏览器核心进程,并通过文件协议加载本地文档路径。为避免阻塞 UI 线程,初始化操作应严格采用异步模式:

using Microsoft.Web.WebView2.Wpf;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace AppCore.Views
{
    public partial class DocumentPreviewPanel : UserControl
    {
        public DocumentPreviewPanel() => InitializeComponent();

        private void ExecuteNavigationBack(object sender, RoutedEventArgs e)
        {
            var navService = NavigationService.GetNavigationService(this);
            if (navService?.CanGoBack == true) navService.GoBack();
        }

        private async void OnBrowserEngineInitialized(object sender, RoutedEventArgs e)
        {
            if (sender is not WebView2 browser) return;

            await browser.EnsureCoreWebView2Async(null);
            
            var targetDocument = new Uri(@"file:///C:/ProjectAssets/TechnicalManual.pdf", UriKind.Absolute);
            browser.CoreWebView2?.Navigate(targetDocument.ToString());
        }
    }
}

方案二:基于 Pdfium 渲染引擎的定制化实现

若需要脱离系统浏览器环境并实现精细的交互控制(如缩放、分页导航、滚动条隐藏),可采用基于 PDFium 的第三方渲染库。通过 NuGet 引入 PdfiumViewer 及对应的原生架构包(如 PdfiumViewer.Native.x86_64)后,可构建具备完整操控面板的视图。

XAML 布局将渲染区域与操作侧边栏分离,并通过数据绑定实时反馈当前页码与缩放比例:

<UserControl x:Class="AppCore.Views.PdfReaderWorkspace"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:pdf="clr-namespace:PdfViewer;assembly=PdfViewer">
    <Grid Background="#FFFFFF">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="50"/>
        </Grid.ColumnDefinitions>
        
        <pdf:PdfRenderer x:Name="MainDocumentRenderer" Grid.Column="0"/>
        
        <StackPanel Grid.Column="1" VerticalAlignment="Center" Margin="0,8" Background="#F0F0F0">
            <Button Content="◀" Click="NavigatePrev" Margin="4" ToolTip="上一页"/>
            <TextBlock Text="{Binding DisplayedPage}" TextAlignment="Center" Margin="4,0" FontSize="11"/>
            <TextBlock Text="/" TextAlignment="Center" FontSize="11"/>
            <TextBlock Text="{Binding ElementName=MainDocumentRenderer, Path=PageCount}" TextAlignment="Center" FontSize="11" Margin="0,0,0,4"/>
            <Button Content="▶" Click="NavigateNext" Margin="4" ToolTip="下一页"/>
            <Separator Margin="0,4"/>
            <Button Content="➕" Click="AdjustScale" Tag="up" Margin="4" ToolTip="放大"/>
            <TextBlock Text="{Binding ScalePercentage}" TextAlignment="Center" FontSize="11" Margin="4,0"/>
            <Button Content="➖" Click="AdjustScale" Tag="down" Margin="4" ToolTip="缩小"/>
            <Separator Margin="0,4"/>
            <Button Content="⬌" Click="SetFitMode" Tag="width" Margin="4" ToolTip="适应宽度"/>
            <Button Content="⬍" Click="SetFitMode" Tag="height" Margin="4" ToolTip="适应高度"/>
        </StackPanel>
    </Grid>
</UserControl>

视图模型层实现 INotifyPropertyChanged 接口,拦截渲染引擎的属性变更事件以驱动 UI 更新。文档加载采用内存流处理,避免直接锁定资源文件:

using PdfViewer;
using PdfViewer.Enums;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Resources;

namespace AppCore.Views
{
    public partial class PdfReaderWorkspace : UserControl, INotifyPropertyChanged
    {
        public PdfReaderWorkspace()
        {
            InitializeComponent();
            DataContext = this;

            MainDocumentRenderer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
            MainDocumentRenderer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
            MainDocumentRenderer.ZoomMode = PdfViewerZoomMode.FitWidth;

            MainDocumentRenderer.PropertyChanged += (_, _) =>
            {
                OnPropertyChanged(nameof(DisplayedPage));
                OnPropertyChanged(nameof(ScalePercentage));
            };

            Loaded += OnWorkspaceReady;
        }

        public int DisplayedPage => MainDocumentRenderer.PageNumber + 1;
        public string ScalePercentage => $"{(int)(MainDocumentRenderer.ZoomFactor * 100)}%";

        private async void OnWorkspaceReady(object sender, RoutedEventArgs e) => await LoadEmbeddedDocument();

        private async Task LoadEmbeddedDocument()
        {
            var resUri = new Uri("Assets/Documentation.pdf", UriKind.Relative);
            var info = Application.GetResourceStream(resUri);
            if (info is null) return;

            using var buffer = new MemoryStream();
            await info.Stream.CopyToAsync(buffer);
            buffer.Position = 0;
            MainDocumentRenderer.Open(buffer);
        }

        private void NavigatePrev(object s, RoutedEventArgs e) => MainDocumentRenderer.GoToPreviousPage();
        private void NavigateNext(object s, RoutedEventArgs e) => MainDocumentRenderer.GoToNextPage();

        private void AdjustScale(object s, RoutedEventArgs e)
        {
            if (s is Button btn && btn.Tag?.ToString() is string direction)
            {
                if (direction == "up") MainDocumentRenderer.ZoomIn();
                else if (direction == "down" && MainDocumentRenderer.ZoomFactor > 0.5) MainDocumentRenderer.ZoomOut();
            }
        }

        private void SetFitMode(object s, RoutedEventArgs e)
        {
            if (s is Button btn && btn.Tag?.ToString() is string mode)
            {
                MainDocumentRenderer.ZoomMode = mode == "width" ? PdfViewerZoomMode.FitWidth : PdfViewerZoomMode.FitHeight;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string prop = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}

相关文章

Trojan服务器搭建与配置

一、整体架构(先对齐认知)Clash Meta (PC / iOS / Android)        ↓ TLS   Trojan Server (443)        ↓     InternetTrojan 的核心是: TLS + HTTPS 流量伪装 看起来像正常网站 非常适合...

Tailscale 的详细用法

Tailscale 是一种基于 WireGuard 协议 的 零配置 VPN(虚拟私有网络)服务,让设备之间能够 安全、加密地直接连接,就像它们在同一个本地网络一样。它的核心特点是 简单、安全、跨平台。Tailscale 非常适合 没有公网 IP、两台电脑不在同一局域网 的场景。 简单来说,Tailscale 是什么?Tailscale 是一款让你的各种设备(电脑、服务器、手机...

Clash Tun 模式 导致 爱快(iKuai SD-Wan)内网域名无法访问

一、Clash  DNS 配置dns:  enable: true  listen: 0.0.0.0:53  ipv6: true  enhanced-mode: redir-host  nameserver:    - 223.5.5.5    - 223.6.6.6iKuai 内网域名 ...

深入解析Node.js运行环境与异步I/O架构

深入解析Node.js运行环境与异步I/O架构

核心定义与价值Node.js本质上是一个JavaScript运行环境,而非编程语言或应用框架。它赋予了JavaScript脱离浏览器在服务端、命令行工具及网络应用中执行的能力。其核心意义在于:用单一语言打通前后端开发壁垒。基于事件驱动与非阻塞I/O的架构特性,Node.js在处理API网关、实时通信及微服务等I/O密集型场景时表现卓越,已成为现代后端工程的主流选择。浏览器沙箱限制1995年Java...

ADO.NET SQL参数化查询的最佳实践

在 ADO.NET 中执行 SQL 查询时,参数化查询是一种关键的安全措施和性能优化手段。它通过将 SQL 命令和用户提供的数据分开处理,有效防止了 SQL 注入攻击,并有助于数据库缓存执行计划。下面总结了几种常用的参数化查询方式。 1. 使用 SqlParameter 对象(推荐) 这是最推荐的参数化查询方式。通过显式创建 SqlParameter 对象,您可以精确控制参数的类...

基于ELK的日志集中化分析系统搭建

构建统一日志管理平台的必要性 在分布式架构中,各服务节点独立运行,日志分散存储于不同主机。传统通过命令行工具如grep、awk逐个检索日志的方式,在数据量庞大时效率极低,难以实现快速定位问题。为提升运维效率,需建立集中式日志处理体系,具备日志采集、传输、存储、分析与告警能力。 ELK技术栈核心组件解析 Elasticsearch:分布式搜索引擎,支持全文检索、实时数据分析和高可用集群部署,...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。