You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF应用中使用PDFsharp显示本地PDF并实现搜索?求代码

在WPF中用PDFsharp实现本地PDF显示与搜索

PDFsharp本身专注于PDF的生成和编辑,没有内置的WPF渲染控件,所以我们需要通过将PDF页面转换为WPF图像来实现显示,同时提取PDF文本完成搜索功能。以下是完整实现步骤:

准备工作

  • 打开你的WPF项目,通过NuGet安装PDFsharp-wpf包(一定要选WPF适配版本,别用GDI+版本,避免兼容性问题)

一、PDF文件加载与显示实现

我们可以把PDF页面转换为WPF的DrawingImage,再放到界面容器中展示:

1. 核心渲染工具类

创建PdfRenderer类,负责PDF页面到WPF图像的转换:

using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using PdfSharp.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public static class PdfRenderer
{
    public static ImageSource RenderPdfPage(string pdfPath, int pageNumber)
    {
        using (PdfDocument document = PdfReader.Open(pdfPath, PdfDocumentOpenMode.Import))
        {
            if (pageNumber < 0 || pageNumber >= document.PageCount)
                throw new ArgumentOutOfRangeException(nameof(pageNumber));

            PdfPage page = document.Pages[pageNumber];
            XSize pageSize = page.Size;

            // 创建WPF绘图容器
            DrawingVisual visual = new DrawingVisual();
            using (DrawingContext dc = visual.RenderOpen())
            {
                // 绑定PDFsharp绘图上下文与WPF绘图上下文
                using (XGraphics xg = XGraphics.FromDrawingContext(dc, new XRect(0, 0, pageSize.Width, pageSize.Height), XGraphicsUnit.Point))
                {
                    XGraphicsPdfRenderer renderer = new XGraphicsPdfRenderer(xg, page);
                    renderer.RenderPage();
                }
            }

            // 将绘图结果转为ImageSource
            RenderTargetBitmap rtb = new RenderTargetBitmap(
                (int)pageSize.Width, (int)pageSize.Height,
                96, 96, PixelFormats.Default);
            rtb.Render(visual);

            return BitmapFrame.Create(rtb);
        }
    }

    // 获取PDF总页数
    public static int GetPdfPageCount(string pdfPath)
    {
        using (PdfDocument document = PdfReader.Open(pdfPath, PdfDocumentOpenMode.Import))
        {
            return document.PageCount;
        }
    }
}

2. WPF界面与加载逻辑

XAML界面包含打开按钮、搜索栏和PDF显示区域:

<Window x:Class="WpfPdfViewer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PDF Viewer" Height="800" Width="1000">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <!-- 操作栏 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="10">
            <TextBox x:Name="txtSearch" Width="300" Margin="0 0 10 0" 
                     Watermark="输入搜索关键词"/>
            <Button x:Name="btnSearch" Content="搜索" Click="BtnSearch_Click"/>
            <Button x:Name="btnOpen" Content="打开PDF" Click="BtnOpen_Click" Margin="10 0 0 0"/>
        </StackPanel>
        <!-- PDF滚动显示区 -->
        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
            <StackPanel x:Name="pnlPdfContent"/>
        </ScrollViewer>
    </Grid>
</Window>

后台代码实现打开PDF和加载页面逻辑:

using Microsoft.Win32;
using System.Windows;

public partial class MainWindow : Window
{
    private string _currentPdfPath;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void BtnOpen_Click(object sender, RoutedEventArgs e)
    {
        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.Filter = "PDF文件 (*.pdf)|*.pdf";
        if (openFileDialog.ShowDialog() == true)
        {
            _currentPdfPath = openFileDialog.FileName;
            LoadPdfPages();
        }
    }

    private void LoadPdfPages()
    {
        pnlPdfContent.Children.Clear();
        int pageCount = PdfRenderer.GetPdfPageCount(_currentPdfPath);

        for (int i = 0; i < pageCount; i++)
        {
            Image image = new Image();
            image.Source = PdfRenderer.RenderPdfPage(_currentPdfPath, i);
            image.Margin = new Thickness(0, 0, 0, 10);
            pnlPdfContent.Children.Add(image);
        }
    }
}

二、PDF内容搜索功能实现

通过提取PDF每页的文本内容,匹配关键词后定位到对应页面:

1. 文本提取工具类

创建PdfTextExtractor类,遍历PDF内容提取文本:

using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
using PdfSharp.Pdf.Content;
using PdfSharp.Pdf.Content.Objects;
using System.Text;

public static class PdfTextExtractor
{
    public static Dictionary<int, string> ExtractAllText(string pdfPath)
    {
        Dictionary<int, string> pageTexts = new Dictionary<int, string>();
        using (PdfDocument document = PdfReader.Open(pdfPath, PdfDocumentOpenMode.Import))
        {
            for (int i = 0; i < document.PageCount; i++)
            {
                PdfPage page = document.Pages[i];
                string text = ExtractTextFromPage(page);
                pageTexts.Add(i, text);
            }
        }
        return pageTexts;
    }

    private static string ExtractTextFromPage(PdfPage page)
    {
        StringBuilder sb = new StringBuilder();
        CObject content = ContentReader.ReadContent(page);
        TraverseContent(content, sb);
        return sb.ToString();
    }

    private static void TraverseContent(CObject obj, StringBuilder sb)
    {
        if (obj is CArray array)
        {
            foreach (CObject item in array)
                TraverseContent(item, sb);
        }
        else if (obj is CSequence sequence)
        {
            foreach (CObject item in sequence)
                TraverseContent(item, sb);
        }
        else if (obj is COperator op)
        {
            switch (op.OpCode.Name)
            {
                case "Tj":
                case "'":
                case "\"":
                    sb.Append(op.Operands[0]);
                    break;
                case "TJ":
                    if (op.Operands[0] is CArray tjArray)
                    {
                        foreach (CObject tjItem in tjArray)
                        {
                            if (tjItem is CString str)
                                sb.Append(str.Value);
                        }
                    }
                    break;
            }
            TraverseContent(op.Operands, sb);
        }
    }
}

2. 搜索逻辑实现

在后台代码中添加搜索按钮点击事件,完成关键词匹配与页面定位:

private void BtnSearch_Click(object sender, RoutedEventArgs e)
{
    if (string.IsNullOrEmpty(_currentPdfPath) || string.IsNullOrEmpty(txtSearch.Text))
        return;

    string keyword = txtSearch.Text.Trim();
    var pageTexts = PdfTextExtractor.ExtractAllText(_currentPdfPath);

    // 筛选包含关键词的页面
    var matchedPages = pageTexts.Where(kv => kv.Value.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0)
                                .Select(kv => kv.Key)
                                .ToList();

    if (matchedPages.Count == 0)
    {
        MessageBox.Show("未找到匹配内容");
        return;
    }

    // 滚动到第一个匹配页面并添加高亮
    if (pnlPdfContent.Children.Count > matchedPages[0])
    {
        UIElement targetPage = pnlPdfContent.Children[matchedPages[0]];
        targetPage.BringIntoView();
        targetPage.Effect = new System.Windows.Media.Effects.DropShadowEffect { Color = Colors.Yellow, BlurRadius = 10 };
        
        // 2秒后移除高亮
        Dispatcher.BeginInvoke(new Action(() =>
        {
            targetPage.Effect = null;
        }), System.Windows.Threading.DispatcherPriority.Background, TimeSpan.FromSeconds(2));
    }

    MessageBox.Show($"找到{matchedPages.Count}个匹配页面,已定位到第一个匹配页");
}

注意事项

  • PDFsharp的文本提取对复杂排版(如嵌入特殊字体、加密PDF)支持有限,若需更稳定的文本提取,可结合专门的PDF文本库,但本示例基于纯PDFsharp实现。
  • 加载大体积PDF时,建议改为分页按需加载,避免内存占用过高。
  • 加密PDF需在PdfReader.Open方法中传入密码参数,才能正常读取内容。

内容的提问来源于stack exchange,提问作者LiangJiechao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 11:09:59