如何在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
相关产品推荐
相关产品推荐

