最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# WPF實現(xiàn)讀取文件夾中的PDF并顯示其頁數(shù)的操作指南

 更新時間:2025年09月24日 10:11:29   作者:Humbunklung  
文章介紹了利用WPF和AI工具Gemini開發(fā)PDF整理工具的過程,采用iText處理PDF、NPOI導出Excel,結合MVVM模式實現(xiàn)UI與邏輯分離,并解決轉換器錯誤問題,最終完成文件信息展示與批量處理功能,需要的朋友可以參考下

工作中需要整理一些PDF格式文件,程序員的存在就是為了讓大家可以“更高效地工作”,而AI的出現(xiàn)就可以讓程序更“高效地工作”,于是求助于很長(我指上下文)的Gemini,它幫助了我快速搭建項目,但也給我留下了坑(見本文“后記”部分),于是我把這個開發(fā)過程記錄了下來。

技術選型

  • UI框架: WPF (.NET 6/7/8 或 .NET Framework 4.7.2+) - 用于構建現(xiàn)代化的Windows桌面應用。
  • PDF處理: iText (替代了舊版的 iTextSharp 及 iText7) - 一個強大且流行的開源PDF處理庫。
  • Excel導出: NPOI - 一個開源的.NET庫,可以讀寫Office文檔,無需安裝Microsoft Office。
  • 設計模式: MVVM - 使UI和業(yè)務邏輯分離,提高代碼的可測試性和復用性。

第一步:創(chuàng)建項目并安裝依賴庫

打開 Visual Studio,創(chuàng)建一個新的 WPF 應用程序 項目(本文為.net 8.0項目)。

通過 NuGet 包管理器安裝以下必要的庫。在“解決方案資源管理器”中右鍵點擊你的項目,選擇“管理NuGet程序包”,然后搜索并安裝:

  • iText
  • NPOI
  • Microsoft.WindowsAPICodePack-Shell (為了一個更好看的文件夾選擇對話框)

第二步:定義數(shù)據(jù)模型 (Model)

這是我們用來存儲每個PDF文件信息的類。

PdfFileInfo.cs

namespace PdfFileScanner
{
    public class PdfFileInfo
    {
        public string FileName { get; set; } = string.Empty;
        public int PageCount { get; set; }
        public string FileSize { get; set; } = string.Empty;
    }
}

第三步:創(chuàng)建視圖模型 (ViewModel)

ViewModel 是連接視圖和模型的橋梁,包含了所有的業(yè)務邏輯和UI狀態(tài),在這里,我按照AI的提示創(chuàng)建了MainViewModel類。

MainViewModel.cs

using iText.Kernel.Pdf;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs; // For modern folder browser

namespace PdfFileScanner
{
    public class MainViewModel : INotifyPropertyChanged
    {
        // INotifyPropertyChanged 實現(xiàn),用于通知UI屬性已更改
        public event PropertyChangedEventHandler? PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        // 存儲PDF文件信息的集合,ObservableCollection能自動通知UI更新
        public ObservableCollection<PdfFileInfo> PdfFiles { get; } = new ObservableCollection<PdfFileInfo>();

        private string _statusText = "請選擇一個文件夾...";
        public string StatusText
        {
            get => _statusText;
            set { _statusText = value; OnPropertyChanged(nameof(StatusText)); }
        }

        private double _progressValue;
        public double ProgressValue
        {
            get => _progressValue;
            set { _progressValue = value; OnPropertyChanged(nameof(ProgressValue)); }
        }

        private bool _isBusy;
        public bool IsBusy
        {
            get => _isBusy;
            set
            {
                _isBusy = value;
                OnPropertyChanged(nameof(IsBusy));
                // 當IsBusy狀態(tài)改變時,通知命令重新評估其能否執(zhí)行
                ((RelayCommand)SelectFolderCommand).RaiseCanExecuteChanged();
                ((RelayCommand)ExportToExcelCommand).RaiseCanExecuteChanged();
            }
        }
        
        // 命令綁定
        public ICommand SelectFolderCommand { get; }
        public ICommand ExportToExcelCommand { get; }

        public MainViewModel()
        {
            SelectFolderCommand = new RelayCommand(async () => await ProcessFolderAsync(), () => !IsBusy);
            ExportToExcelCommand = new RelayCommand(ExportToExcel, () => PdfFiles.Count > 0 && !IsBusy);
        }

        private async Task ProcessFolderAsync()
        {
            // 使用現(xiàn)代化的文件夾選擇對話框
            var dialog = new CommonOpenFileDialog
            {
                IsFolderPicker = true,
                Title = "請選擇包含PDF文件的文件夾"
            };

            if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
            {
                string selectedPath = dialog.FileName;
                IsBusy = true;
                StatusText = "正在準備處理...";
                PdfFiles.Clear();
                ProgressValue = 0;

                await Task.Run(() => // 在后臺線程執(zhí)行耗時操作,避免UI卡死
                {
                    var files = Directory.GetFiles(selectedPath, "*.pdf");
                    int processedCount = 0;

                    foreach (var file in files)
                    {
                        processedCount++;
                        var progressPercentage = (double)processedCount / files.Length * 100;
                        
                        // 更新UI元素必須在UI線程上執(zhí)行
                        Application.Current.Dispatcher.Invoke(() =>
                        {
                            StatusText = $"正在處理: {Path.GetFileName(file)} ({processedCount}/{files.Length})";
                            ProgressValue = progressPercentage;
                        });

                        try
                        {
                            // 獲取文件信息
                            var fileInfo = new FileInfo(file);
                            int pageCount = 0;

                            // 使用 iText7 讀取PDF頁數(shù)
                            using (var pdfReader = new PdfReader(file))
                            {
                                using (var pdfDoc = new PdfDocument(pdfReader))
                                {
                                    pageCount = pdfDoc.GetNumberOfPages();
                                }
                            }
                            
                            // 創(chuàng)建模型對象并添加到集合中
                            var pdfData = new PdfFileInfo
                            {
                                FileName = fileInfo.Name,
                                PageCount = pageCount,
                                FileSize = $"{fileInfo.Length / 1024.0:F2} KB" // 格式化文件大小
                            };

                            Application.Current.Dispatcher.Invoke(() => PdfFiles.Add(pdfData));
                        }
                        catch (System.Exception ex)
                        {
                            // 如果某個PDF文件損壞,記錄錯誤并繼續(xù)
                            Application.Current.Dispatcher.Invoke(() =>
                            {
                                StatusText = $"處理文件 {Path.GetFileName(file)} 時出錯: {ex.Message}";
                            });
                        }
                    }
                });

                StatusText = $"處理完成!共找到 {PdfFiles.Count} 個PDF文件。";
                IsBusy = false;
            }
        }

        private void ExportToExcel()
        {
            var saveFileDialog = new SaveFileDialog
            {
                Filter = "Excel 工作簿 (*.xlsx)|*.xlsx",
                FileName = $"PDF文件列表_{System.DateTime.Now:yyyyMMddHHmmss}.xlsx"
            };

            if (saveFileDialog.ShowDialog() == true)
            {
                try
                {
                    // 使用 NPOI 創(chuàng)建 Excel
                    IWorkbook workbook = new XSSFWorkbook();
                    ISheet sheet = workbook.CreateSheet("PDF文件信息");

                    // 創(chuàng)建表頭
                    IRow headerRow = sheet.CreateRow(0);
                    headerRow.CreateCell(0).SetCellValue("文件名");
                    headerRow.CreateCell(1).SetCellValue("頁數(shù)");
                    headerRow.CreateCell(2).SetCellValue("文件大小 (KB)");

                    // 填充數(shù)據(jù)
                    for (int i = 0; i < PdfFiles.Count; i++)
                    {
                        IRow dataRow = sheet.CreateRow(i + 1);
                        dataRow.CreateCell(0).SetCellValue(PdfFiles[i].FileName);
                        dataRow.CreateCell(1).SetCellValue(PdfFiles[i].PageCount);
                        dataRow.CreateCell(2).SetCellValue(PdfFiles[i].FileSize);
                    }
                    
                    // 自動調整列寬
                    sheet.AutoSizeColumn(0);
                    sheet.AutoSizeColumn(1);
                    sheet.AutoSizeColumn(2);

                    // 寫入文件
                    using (var fs = new FileStream(saveFileDialog.FileName, FileMode.Create, FileAccess.Write))
                    {
                        workbook.Write(fs);
                    }
                    
                    MessageBox.Show("成功導出到Excel!", "導出成功", MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show($"導出失敗: {ex.Message}", "錯誤", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }
    }

    // 一個簡單的ICommand實現(xiàn)
    public class RelayCommand : ICommand
    {
        private readonly System.Action _execute;
        private readonly System.Func<bool>? _canExecute;

        public event System.EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public RelayCommand(System.Action execute, System.Func<bool>? canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object? parameter) => _canExecute == null || _canExecute();
        public void Execute(object? parameter) => _execute();
        public void RaiseCanExecuteChanged() => CommandManager.InvalidateRequerySuggested();
    }
}

第四步:設計用戶界面 (View)

這是 MainWindow.xaml 文件,定義了程序窗口的布局和控件,并將它們綁定到 ViewModel。

MainWindow.xaml

<Window x:Class="PdfFileScanner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PdfFileScanner"
        mc:Ignorable="d"
        Title="PDF文件掃描器" Height="600" Width="800" MinHeight="400" MinWidth="600">
    
    <!-- 設置窗口的數(shù)據(jù)上下文為ViewModel -->
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- 頂部操作欄 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
            <Button Content="選擇文件夾" Command="{Binding SelectFolderCommand}" Padding="15,5" FontSize="14" IsEnabled="{Binding !IsBusy}"/>
            <Button Content="導出到Excel" Command="{Binding ExportToExcelCommand}" Margin="10,0,0,0" Padding="15,5" FontSize="14" IsEnabled="{Binding !IsBusy}"/>
        </StackPanel>

        <!-- 文件列表 -->
        <DataGrid Grid.Row="1" ItemsSource="{Binding PdfFiles}" AutoGenerateColumns="False" 
                  CanUserAddRows="False" IsReadOnly="True" FontSize="14">
            <DataGrid.Columns>
                <DataGridTextColumn Header="文件名" Binding="{Binding FileName}" Width="*"/>
                <DataGridTextColumn Header="頁數(shù)" Binding="{Binding PageCount}" Width="Auto"/>
                <DataGridTextColumn Header="文件大小" Binding="{Binding FileSize}" Width="Auto"/>
            </DataGrid.Columns>
        </DataGrid>

        <!-- 底部狀態(tài)欄和進度條 -->
        <Grid Grid.Row="2" Margin="0,10,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="200"/>
            </Grid.ColumnDefinitions>

            <TextBlock Grid.Column="0" Text="{Binding StatusText}" VerticalAlignment="Center" 
                       TextTrimming="CharacterEllipsis"/>

            <ProgressBar Grid.Column="1" Value="{Binding ProgressValue}" Maximum="100" Height="20"
                         Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        </Grid>
    </Grid>
</Window>

MainWindow.xaml.cs (代碼隱藏文件)
這里我們只需要確保 DataContext 被正確設置。上面的XAML已經(jīng)通過 <local:MainViewModel/> 標簽完成了這一步,所以代碼隱藏文件非常干凈。

using System.Windows;

namespace PdfFileScanner
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // DataContext 在 XAML 中設置,這里無需代碼
        }
    }
}

總結與解釋

  1. 文件夾選擇: 點擊“選擇文件夾”按鈕,會觸發(fā) SelectFolderCommand。我們使用了 Microsoft.WindowsAPICodePack-Shell 庫,它提供了一個比默認的 FolderBrowserDialog 更現(xiàn)代、更友好的對話框。
  2. 后臺處理與進度更新:
    • 核心的PDF文件處理邏輯被包裹在 Task.Run() 中,這會將其放到一個后臺線程上執(zhí)行,防止UI線程(負責渲染窗口和響應用戶操作的線程)被阻塞而導致程序“未響應”。
    • 在后臺線程中,我們不能直接修改UI控件(如 ProgressBarTextBlock)或綁定到UI的集合(如 ObservableCollection)。因此,我們使用 Application.Current.Dispatcher.Invoke() 將這些更新操作“派發(fā)”回UI線程執(zhí)行,這是WPF中進行跨線程UI更新的標準做法。
    • IsBusy 屬性用來控制UI狀態(tài)。當 IsBusytrue 時,按鈕會被禁用,進度條會顯示。
  3. 信息提取:
    • 文件名和大小: 使用 System.IO.FileInfo 類可以輕松獲取。
    • PDF頁數(shù): 使用 iText 7 庫。我們通過 PdfReaderPdfDocument 對象打開PDF文件,然后調用 GetNumberOfPages() 方法。using 語句確保文件流被正確關閉和釋放。
  4. 列表展示:
    • WPF的 DataGrid 控件的 ItemsSource 屬性被綁定到 ViewModel 中的 ObservableCollection<PdfFileInfo> 集合。
    • ObservableCollection 的美妙之處在于,每當你向其中 AddRemove 一個項時,它會自動通知綁定的 DataGrid 更新,無需手動刷新。
  5. Excel導出:
    • 點擊“導出到Excel”按鈕會觸發(fā) ExportToExcelCommand。
    • 該命令首先會彈出一個標準的“文件保存”對話框,讓用戶選擇保存位置和文件名。
    • 然后,它使用 NPOI 庫在內存中創(chuàng)建一個Excel工作簿 (XSSFWorkbook 對應 .xlsx格式),創(chuàng)建工作表、表頭行,然后遍歷 PdfFiles 集合,將每條數(shù)據(jù)寫入新的一行。
    • 最后,將內存中的工作簿寫入到用戶選擇的文件流中。

這個方案完整地實現(xiàn)了你要求的所有功能,并且采用了現(xiàn)代C#和WPF的最佳實踐,代碼結構清晰,易于擴展和維護。

后記

關于轉換器的錯誤

Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}" 改代碼因沒有轉換器Converter而出錯,故需自定義一個轉換器:

添加轉換器類BooleanToVisibilityConverter

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool booleanValue)
        {
            if (booleanValue)
            {
                return Visibility.Visible;
            }
            else
            {
                // Default to Collapsed, or Hidden based on 'parameter' or another property
                return Visibility.Collapsed;
            }
        }
        return Visibility.Visible; // Default if not a boolean
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException(); // Usually not needed for Visibility conversion
    }
}

然后在 MainWindow.xaml 中注冊這個轉換器:

    <!-- 在這里添加資源定義 -->
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Window.Resources>

修改后的MainWindow.xaml文件如下:

<Window x:Class="PdfFileScanner.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PdfFileScanner"
        mc:Ignorable="d"
        Title="PDF文件掃描器" Height="600" Width="800" MinHeight="400" MinWidth="600">

    <!-- 設置窗口的數(shù)據(jù)上下文為ViewModel -->
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <!-- 在這里添加資源定義 -->
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Window.Resources>
    
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!-- 頂部操作欄 -->
        <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
            <Button Content="選擇文件夾" Command="{Binding SelectFolderCommand}" Padding="15,5" FontSize="14" IsEnabled="{Binding !IsBusy}"/>
            <Button Content="導出到Excel" Command="{Binding ExportToExcelCommand}" Margin="10,0,0,0" Padding="15,5" FontSize="14" IsEnabled="{Binding !IsBusy}"/>
        </StackPanel>

        <!-- 文件列表 -->
        <DataGrid Grid.Row="1" ItemsSource="{Binding PdfFiles}" AutoGenerateColumns="False" 
                  CanUserAddRows="False" IsReadOnly="True" FontSize="14">
            <DataGrid.Columns>
                <DataGridTextColumn Header="文件名" Binding="{Binding FileName}" Width="*"/>
                <DataGridTextColumn Header="頁數(shù)" Binding="{Binding PageCount}" Width="Auto"/>
                <DataGridTextColumn Header="文件大小" Binding="{Binding FileSize}" Width="Auto"/>
            </DataGrid.Columns>
        </DataGrid>

        <!-- 底部狀態(tài)欄和進度條 -->
        <Grid Grid.Row="2" Margin="0,10,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="200"/>
            </Grid.ColumnDefinitions>

            <TextBlock Grid.Column="0" Text="{Binding StatusText}" VerticalAlignment="Center" 
                       TextTrimming="CharacterEllipsis"/>

            <ProgressBar Grid.Column="1" Value="{Binding ProgressValue}" Maximum="100" Height="20"
                         Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        </Grid>
    </Grid>
</Window>

問題解決!

運行效果如下:

以上就是C# WPF實現(xiàn)讀取文件夾中的PDF并顯示其頁數(shù)的操作指南的詳細內容,更多關于C# WPF讀取文件夾PDF并顯示其頁數(shù)的資料請關注腳本之家其它相關文章!

相關文章

最新評論

阿瓦提县| 通化市| 共和县| 平凉市| 融水| 崇明县| 靖州| 丘北县| 大竹县| 襄汾县| 贺州市| 政和县| 福海县| 定南县| 兴仁县| 南康市| 揭西县| 龙泉市| 雷州市| 兴仁县| 台中县| 临朐县| 平安县| 安阳县| 东乡族自治县| 米脂县| 唐山市| 青浦区| 年辖:市辖区| 双辽市| 榆树市| 平阴县| 资兴市| 遵义县| 苏尼特右旗| 甘洛县| 吴忠市| 阿拉善右旗| 许昌县| 茌平县| 保亭|