Install-Package iTextSharp;
public ICommand ExportAsPDFCommand { get; private set; }ExportAsPDFCommand = new DelCommand(async (obj) => await ExportAsPDFCommandExecuted(obj));private async Task ExportAsPDFCommandExecuted(object? obj) {try{exportedIdx = 0;var dg = obj as DataGrid;var items = dg.Items.Cast<Book>()?.ToList();batchCount = items.Count() % batchSize > 0 ? items.Count() / batchSize + 1 : items.Count() / batchSize;SaveFileDialog dialog = new SaveFileDialog();dialog.Filter = $"PDF Files|*.pdf";dialog.FileName = $"PDF_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}_{Guid.NewGuid():N}";if (dialog.ShowDialog() == true){await Task.Run(async () =>{await ExportListDataInPDFAsync(items, dialog.FileName);await Application.Current.Dispatcher.InvokeAsync(() =>{MessageBox.Show($"Exported file in {dialog.FileName}!");});});}}catch (Exception ex){MessageBox.Show(ex.Message); } }private async Task ExportListDataInPDFAsync<T>(List<T> dataList, string pdfFileName) { await Task.Run(async() =>{exportedIdx = 0;await PopulateStatusMsg($"Start exporting in pdf");//Create document and writerusing (var pdfDoc = new Document()){using (var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream(pdfFileName, FileMode.Create))){pdfDoc.Open();// Add titlevar titleFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 18);var titleParagraph = new Paragraph($"Data Export - {typeof(T).Name}", titleFont){Alignment = Element.ALIGN_CENTER,SpacingAfter = 20f};pdfDoc.Add(titleParagraph);// Add export datevar dateFont = FontFactory.GetFont(FontFactory.HELVETICA, 10);var dateParagraph = new Paragraph($"Exported on: {DateTime.Now:yyyy-MM-dd HH:mm:ss}", dateFont){Alignment = Element.ALIGN_RIGHT,SpacingAfter = 15f};pdfDoc.Add(dateParagraph);if (dataList == null || !dataList.Any()){// Handle empty listvar emptyFont = FontFactory.GetFont(FontFactory.HELVETICA_OBLIQUE, 12);var emptyParagraph = new Paragraph("No data available to export.", emptyFont){Alignment = Element.ALIGN_CENTER,SpacingAfter = 20f};pdfDoc.Add(emptyParagraph);}else{// Create table with columns based on object propertiesvar props = typeof(T).GetProperties();var table = new PdfPTable(props.Length){WidthPercentage = 100,SpacingBefore = 10f,SpacingAfter = 10f};// Set column widths (optional - adjust as needed)float[] columnWidths = new float[props.Length];for (int i = 0; i < props.Length; i++){columnWidths[i] = 1f;}table.SetWidths(columnWidths);// Add header rowvar headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 10);foreach (var prop in props){var cell = new PdfPCell(new Phrase(prop.Name, headerFont)){BackgroundColor = new BaseColor(220, 220, 220),HorizontalAlignment = Element.ALIGN_CENTER,Padding = 5f};table.AddCell(cell);}// Add data rowsvar dataFont = FontFactory.GetFont(FontFactory.HELVETICA, 9);foreach (var item in dataList){foreach (var property in props){var value = property.GetValue(item)?.ToString() ?? string.Empty;var cell = new PdfPCell(new Phrase(value, dataFont)){Padding = 4f,HorizontalAlignment =(int) HorizontalAlignment.Center};table.AddCell(cell);}if(++exportedIdx%100000==0){await PopulateStatusMsg($"Exported {exportedIdx} items in pdf");}}await PopulateStatusMsg($"Adding table...");pdfDoc.Add(table);// Add summaryvar summaryFont = FontFactory.GetFont(FontFactory.HELVETICA, 10);var summaryParagraph = new Paragraph($"Total records: {dataList.Count}", summaryFont){Alignment = Element.ALIGN_RIGHT,SpacingBefore = 10f};pdfDoc.Add(summaryParagraph);}pdfDoc.Close();await PopulateStatusMsg($"Expoted in {pdfFileName} pdf file successfully");}}}); }
//App.xaml <Application x:Class="WpfApp31.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp31"><Application.Resources></Application.Resources> </Application>//App.xaml.cs using Microsoft.Extensions.DependencyInjection; using System.Configuration; using System.Data; using System.Windows;namespace WpfApp31 {/// <summary>/// Interaction logic for App.xaml/// </summary>public partial class App : Application{IServiceProvider serviceProvider;protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);var services = new ServiceCollection();ConfigureServices(services);serviceProvider = services.BuildServiceProvider();var mainWin=serviceProvider.GetRequiredService<MainWindow>();mainWin?.Show();}private void ConfigureServices(ServiceCollection services){services.AddSingleton<IIDService, IDService>();services.AddSingleton<INameService, NameService>();services.AddSingleton<IISBNService, ISBNService>();services.AddSingleton<MainWindow>();services.AddSingleton<MainVM>();}}}//MainWindow.xaml <Window x:Class="WpfApp31.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:WpfApp31"WindowState="Maximized"mc:Ignorable="d"Title="{Binding MainTitle}" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition Height="Auto"/></Grid.RowDefinitions><DataGrid Grid.Row="0"ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"VirtualizingPanel.IsVirtualizing="True"VirtualizingPanel.VirtualizationMode="Recycling"VirtualizingPanel.CacheLength="2,2"VirtualizingPanel.CacheLengthUnit="Item"ScrollViewer.IsDeferredScrollingEnabled="True"ScrollViewer.CanContentScroll="True"AutoGenerateColumns="False"CanUserAddRows="False"><DataGrid.Columns><DataGridTemplateColumn><DataGridTemplateColumn.CellTemplate><DataTemplate><Grid Width="{Binding DataContext.GridWidth,RelativeSource={RelativeSource AncestorType=Window}}"Height="{Binding DataContext.GridHeight,RelativeSource={RelativeSource AncestorType=Window}}"><Grid.Background><ImageBrush ImageSource="{Binding ImgSource}" Stretch="Uniform"/></Grid.Background><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.Resources><Style TargetType="TextBlock"><Setter Property="FontSize" Value="30"/><Style.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="FontSize" Value="50"/><Setter Property="Foreground" Value="Red"/></Trigger></Style.Triggers></Style></Grid.Resources><TextBlock Text="{Binding Id}" Grid.Row="0" Grid.Column="0"/><TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1"/><TextBlock Text="{Binding Title}" Grid.Row="0" Grid.Column="2"/><TextBlock Text="{Binding Topic}" Grid.Row="0" Grid.Column="3"/><TextBlock Text="{Binding ISBN}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="4"HorizontalAlignment="Center"/></Grid></DataTemplate></DataGridTemplateColumn.CellTemplate></DataGridTemplateColumn></DataGrid.Columns><DataGrid.ContextMenu><ContextMenu><MenuItem Header="Export As Excel"Command="{Binding ExportAsExcelCommand}"CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget}"/><MenuItem Header="Export As PDF"Command="{Binding ExportAsPDFCommand}"CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},Path=PlacementTarget}"/></ContextMenu></DataGrid.ContextMenu></DataGrid><TextBlock Grid.Row="1"Text="{Binding StatusMsg}"FontSize="30"/></Grid> </Window>//MainWindow.xaml.cs using CommunityToolkit.Mvvm.ComponentModel; using iTextSharp.text; using iTextSharp.text.pdf; using Microsoft.Win32; using OfficeOpenXml; using System.Collections.Concurrent; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection.Metadata; using System.Runtime.CompilerServices; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Document = iTextSharp.text.Document; using Paragraph = iTextSharp.text.Paragraph;namespace WpfApp31 {/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(MainVM vm){InitializeComponent();this.DataContext = vm;this.Loaded += async (s, e) =>{vm.GridWidth = this.ActualWidth;vm.GridHeight = this.ActualHeight / 2;await vm.InitPopulateAsync();};}}public partial class MainVM : ObservableObject{IIDService idService;INameService nameService;IISBNService isbnService;List<string> imgsList;int imgsCount = 0;int imgsIdx = 0;int batchSize = 1_000_000;int batchCount = 0;int exportedIdx = 0;private ConcurrentDictionary<string, ImageSource> imgDicCache = new ConcurrentDictionary<string, ImageSource>();public ICommand ExportAsExcelCommand { get; private set; }public ICommand ExportAsPDFCommand { get; private set; }public MainVM(IIDService idServiceValue, INameService nameServiceValue, IISBNService isbnServiceValue){idService = idServiceValue;nameService = nameServiceValue;isbnService = isbnServiceValue;MainTitle = $"Now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}";ExportAsExcelCommand = new DelCommand(async (obj) => await ExportAsExcelCommandExecuted(obj));ExportAsPDFCommand = new DelCommand(async (obj) => await ExportAsPDFCommandExecuted(obj));InitImgsList();Task.Run(() =>{InitTimer();});}private async Task ExportAsPDFCommandExecuted(object? obj){try{exportedIdx = 0;var dg = obj as DataGrid;var items = dg.Items.Cast<Book>()?.ToList();batchCount = items.Count() % batchSize > 0 ? items.Count() / batchSize + 1 : items.Count() / batchSize;SaveFileDialog dialog = new SaveFileDialog();dialog.Filter = $"PDF Files|*.pdf";dialog.FileName = $"PDF_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}_{Guid.NewGuid():N}";if (dialog.ShowDialog() == true){await Task.Run(async () =>{await ExportListDataInPDFAsync(items, dialog.FileName);await Application.Current.Dispatcher.InvokeAsync(() =>{MessageBox.Show($"Exported file in {dialog.FileName}!");});});}}catch (Exception ex){MessageBox.Show(ex.Message); }}private async Task ExportListDataInPDFAsync<T>(List<T> dataList, string pdfFileName){ await Task.Run(async() =>{exportedIdx = 0;await PopulateStatusMsg($"Start exporting in pdf");//Create document and writerusing (var pdfDoc = new Document()){using (var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream(pdfFileName, FileMode.Create))){pdfDoc.Open();// Add titlevar titleFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 18);var titleParagraph = new Paragraph($"Data Export - {typeof(T).Name}", titleFont){Alignment = Element.ALIGN_CENTER,SpacingAfter = 20f};pdfDoc.Add(titleParagraph);// Add export datevar dateFont = FontFactory.GetFont(FontFactory.HELVETICA, 10);var dateParagraph = new Paragraph($"Exported on: {DateTime.Now:yyyy-MM-dd HH:mm:ss}", dateFont){Alignment = Element.ALIGN_RIGHT,SpacingAfter = 15f};pdfDoc.Add(dateParagraph);if (dataList == null || !dataList.Any()){// Handle empty listvar emptyFont = FontFactory.GetFont(FontFactory.HELVETICA_OBLIQUE, 12);var emptyParagraph = new Paragraph("No data available to export.", emptyFont){Alignment = Element.ALIGN_CENTER,SpacingAfter = 20f};pdfDoc.Add(emptyParagraph);}else{// Create table with columns based on object propertiesvar props = typeof(T).GetProperties();var table = new PdfPTable(props.Length){WidthPercentage = 100,SpacingBefore = 10f,SpacingAfter = 10f};// Set column widths (optional - adjust as needed)float[] columnWidths = new float[props.Length];for (int i = 0; i < props.Length; i++){columnWidths[i] = 1f;}table.SetWidths(columnWidths);// Add header rowvar headerFont = FontFactory.GetFont(FontFactory.HELVETICA_BOLD, 10);foreach (var prop in props){var cell = new PdfPCell(new Phrase(prop.Name, headerFont)){BackgroundColor = new BaseColor(220, 220, 220),HorizontalAlignment = Element.ALIGN_CENTER,Padding = 5f};table.AddCell(cell);}// Add data rowsvar dataFont = FontFactory.GetFont(FontFactory.HELVETICA, 9);foreach (var item in dataList){foreach (var property in props){var value = property.GetValue(item)?.ToString() ?? string.Empty;var cell = new PdfPCell(new Phrase(value, dataFont)){Padding = 4f,HorizontalAlignment =(int) HorizontalAlignment.Center};table.AddCell(cell);}if(++exportedIdx%100000==0){await PopulateStatusMsg($"Exported {exportedIdx} items in pdf");}}await PopulateStatusMsg($"Adding table...");pdfDoc.Add(table);// Add summaryvar summaryFont = FontFactory.GetFont(FontFactory.HELVETICA, 10);var summaryParagraph = new Paragraph($"Total records: {dataList.Count}", summaryFont){Alignment = Element.ALIGN_RIGHT,SpacingBefore = 10f};pdfDoc.Add(summaryParagraph);}pdfDoc.Close();await PopulateStatusMsg($"Expoted in {pdfFileName} pdf file successfully");}}});}private async Task ExportAsExcelCommandExecuted(object? obj){try{await PopulateStatusMsg($"Start exporting");exportedIdx = 0;var dg = obj as DataGrid;var items = dg.Items.Cast<Book>()?.ToList();batchCount = items.Count() % batchSize > 0 ? items.Count() / batchSize + 1 : items.Count() / batchSize;SaveFileDialog dialog = new SaveFileDialog();dialog.Filter = $"Excel Files|*.xlsx";dialog.FileName = $"Excel_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}_{Guid.NewGuid():N}";if (dialog.ShowDialog() == true){await Task.Run(async () =>{await ExportListDataInExcelAsync(items, dialog.FileName);await Application.Current.Dispatcher.InvokeAsync(() =>{MessageBox.Show($"Exported file in {dialog.FileName}!");});});}}catch (Exception ex){MessageBox.Show(ex.Message); } }private async Task ExportListDataInExcelAsync<T>(List<T> dataList, string excelFileName){EPPlusLicense license = new EPPlusLicense();license.SetNonCommercialPersonal("Fred");using (ExcelPackage package = new ExcelPackage()){var props = typeof(T).GetProperties();for (int i = 0; i < batchCount; i++){var tempBooksList = dataList.Skip(i * batchSize).Take(batchSize).ToList();var workSheet = package.Workbook.Worksheets.Add($"Sheet_{i + 1}");for (int j = 0; j < props.Count(); j++){workSheet.Cells[1, j + 1].Value = props[j].Name;}for (int row = 0; row < tempBooksList.Count; row++){++exportedIdx;for (int col = 0; col < props.Length; col++){var value = props[col].GetValue(tempBooksList[row]);workSheet.Cells[row + 2, col + 1].Value = value;}}// Format headersusing (var range = workSheet.Cells[1, 1, 1, props.Length]){range.Style.Font.Bold = true;range.Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;range.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightBlue);}// Auto-fit columns workSheet.Cells[workSheet.Dimension.Address].AutoFitColumns();await PopulateStatusMsg($"Exported {exportedIdx} items");}await PopulateStatusMsg($"Writing in {excelFileName} items");package.SaveAs(new FileInfo(excelFileName));}await PopulateStatusMsg($"Exported in {excelFileName} successfully!");}private async Task PopulateStatusMsg(string msgValue){await Application.Current.Dispatcher.InvokeAsync(() =>{StatusMsg = msgValue;});}private void InitImgsList(){var imgDir = @"../../../Images";if (!Directory.Exists(imgDir)){return;}imgsList = new List<string>(Directory.GetFiles(imgDir));imgsCount = imgsList.Count;}public async Task InitPopulateAsync(){BooksCollection = new ObservableCollection<Book>();List<Book> booksList = new List<Book>();for (int i = 1; i < 1000101; i++){booksList.Add(new Book(){Id = idService.GetID(),Name = nameService.GetName(),ISBN = isbnService.GetISBN(),Title = $"Title_{i}",Topic = $"Topic_{i}",ImgSource = GetImgSourceViaUrl(imgsList[imgsIdx % imgsCount])});++imgsIdx;if (i % 100000 == 0){await PopulateBooksCollectionAsync(booksList);}}if (booksList.Any()){await PopulateBooksCollectionAsync(booksList);}StatusMsg = $"Loaded completely,now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}," +$"loaded {BooksCollection.Count} items,memory:{GetMemory()}";}private ImageSource GetImgSourceViaUrl(string imgUrl){if (!File.Exists(imgUrl)){return null;}if (imgDicCache.TryGetValue(imgUrl, out var img)){return img;}BitmapImage bmi = new BitmapImage();bmi.BeginInit();bmi.UriSource = new Uri(imgUrl, UriKind.RelativeOrAbsolute);bmi.EndInit();bmi.CacheOption = BitmapCacheOption.OnDemand;if (bmi.CanFreeze){bmi.Freeze();}imgDicCache[imgUrl] = bmi;return bmi;}private async Task PopulateBooksCollectionAsync(List<Book> booksList){var tempBooks = booksList.ToList();booksList.Clear();await Application.Current.Dispatcher.InvokeAsync(() =>{foreach (var bk in tempBooks){BooksCollection.Add(bk);}StatusMsg = $"Now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}," +$"loaded {BooksCollection.Count} items,memory:{GetMemory()}";}, System.Windows.Threading.DispatcherPriority.Background);}private string GetMemory(){var memory = Process.GetCurrentProcess().PrivateMemorySize64;return $"{(memory / 1024 / 1024).ToString("#,##0.00")} M";}private void InitTimer(){System.Timers.Timer tmr = new System.Timers.Timer();tmr.Interval = 1000;tmr.Elapsed += Tmr_Elapsed;tmr.Start();}private void Tmr_Elapsed(object? sender, System.Timers.ElapsedEventArgs e){MainTitle = $"Now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff")}";}[ObservableProperty]private ObservableCollection<Book> booksCollection;[ObservableProperty]private string mainTitle;[ObservableProperty]private double gridWidth;[ObservableProperty]private double gridHeight;[ObservableProperty]private string statusMsg;}public class DelCommand : ICommand{private Action<object?> execute;private Predicate<object?> canExecute;public DelCommand(Action<object?> executeValue, Predicate<object?> canExecuteValue = null){execute = executeValue;canExecute = canExecuteValue;}public event EventHandler? CanExecuteChanged{add{CommandManager.RequerySuggested += value;}remove{CommandManager.RequerySuggested -= value;}}public bool CanExecute(object? parameter){return canExecute == null ? true : canExecute(parameter);}public void Execute(object? parameter){execute(parameter);}}public class Book{public int Id { get; set; }public string Name { get; set; }public string ISBN { get; set; }public string Title { get; set; }public string Topic { get; set; }public ImageSource ImgSource { get; set; }}public interface IIDService{int GetID();}public class IDService : IIDService{int idx = 0;public int GetID(){return Interlocked.Increment(ref idx);}}public interface INameService{string GetName();}public class NameService : INameService{int idx = 0;public string GetName(){return $"Name_{++idx}";}}public interface IISBNService{string GetISBN();}public class ISBNService : IISBNService{int idx = 0;public string GetISBN(){return $"ISBN_{++idx}_{Guid.NewGuid():N}";}} }