DBMS Tabular Data: Generate PDF in WPF App
An example of using GitHub Copilot to generate code. In particular to generate Pdf documents from tabular data in a WPF app.
This code is part of the AthStitcher app that is being developed for photo-timing of athletics meets. The app manages data for Meets, Events, Heats and Lane Results. The data is stored in a Sqlite database via Entity Framework Core.
AthStitcher app is able to generate a Photo Finish image by “stitching” together the middle pixels of video frames from the video of the finish of an Athletics event. The horizontal axis of the image represents time. A DBMS system has been added to the functionality of the app to manage processing of the results in the context of Meets, Events, Heats and Lane Results.
- A Meet has Events.
- An Event has Heats.
- A Heat has Lane Results.
Links
Nb: With the code links below, when clicked upon, when the browser shows, click on the contents. Wait a moment whilst it navigates to the method.
Given app generates the results for a Heat as text:
string PrintHeat(Meet CurrentMeet, AthStitcher.Data.Event CurrentEvent, Heat CurrentHeat)
{
if (athStitcherViewModel.DataContext is not AthStitcherGUI.ViewModels.AthStitcherModel vm)
return "";
string printHeader = "\n";
printHeader = $"{CurrentEvent}\t" +
$"Heat No: {CurrentHeat.HeatNo}\n" +
"------------------------------------------------------";
bool tabbed = vm.Scheduling.UseTabbedPrinting;
List<LaneResult> results = CurrentHeat.Results
.OrderBy(r => r.ResultSeconds ?? double.MaxValue) // nulls last
.ToList();
printHeader += $"\nPosn\t{LaneResult.TabHeader()}";
int posn = 1;
foreach (var lr in results)
{
printHeader += "\n" + $"{posn++}\t{lr.ToTab()}";
}
printHeader += "\n\n";
return printHeader;
}
The Heat Pdf
GitHub Copilot in Visual Studio was asked:
Query: Please generate the Heat result as a PDf
Response:
Install-Package QuestPDF
Code:
public static void ExportHeatToPdf( ... )
{
/// <summary>
/// Exports a single heat to a PDF containing header, optional stitched image and a results table.
/// </summary>
public static void ExportHeatToPdf()
{
...
}
Code in Data/topdf.cs in ExportHeatToPdf()
Note this is the final version of the class file.
There was then code for MainWindow.xaml.cs in PrintOneHeatAsPdf() that calls ExportHeatToPdf() after prompting via a dialog to get the pdf filename and location; the prompt makes a suggestion for the filename given the Meet/Event/Heat context.
The page Header was tweaked for my context (by me):
page.Header().PaddingBottom(5).Column(column =>
{
column.Item().Text($"{meet}").SemiBold().FontSize(16);
column.Item().Text($"{ev} — Heat {heat.HeatNo}").FontSize(12);
column.Item().PaddingTop(6).LineHorizontal(1).LineColor(Colors.Grey.Lighten1);
});
Note that Meet and Event classes have an Override toString() method that presents the required class method properties in one string.
Query: Does this page Header appear on al pages if the context was spread over multiple pages. Answer YES
Response Table headers defined with table.Header(…) are also repeated automatically on subsequent pages when the table breaks across pages**
Query: Requested adding a Posn (Position) column and sorting of Results on Time
Was implemented. in ExportHeatToPdf() 😄
QuestPDF
- The QuestPDF NuGet package as used with AthStitcher WPF app to enable export of Heat, Event results as Pdf file.
- Requires licensing acceptance on first use.
- See QuestPDF Licensing
- If accepting license for Community Non Profit use then uncomment line #46 in Data/ToPdf.cs
The Event Pdf
Query: OK we can ow print one heat to pdf. Can we now print all heats in aan event, Part of the infrastructure is done. For PDF we call Print_PDF_Click() which has a switch statement for each pdf type. Heat branch is complete calling PrintOneHeatAsPdf(vm.CurrentMeet, vm.CurrentEvent, heat);() When printing heats for Current Event want them to be continuous PrintOneHeatAsPdf(vm.CurrentMeet, vm.CurrentEvent, heat);()
The method was generated:
/// <summary>
/// Export all heats for a given event into a single continuous PDF.
/// Each heat starts on a fresh page; table headers repeat inside each heat's table.
/// </summary>
public static void ExportEventToPdf( ... )
{
Complete Code in Data/ToPdf.cs in ExportEventToPdf()
Note this is the final version of the class file.`
There was then code for MainWindow.xaml.cs in PrintOneHeatAsPdf() that calls ExportEventToPdf(). 😄
The Meet Pdf
Initially it was thought that the simplest way to print a Meet would be to loop through its Events and call ExportEventToPdf() for each Event. However this would generate multiple PDF files, one per Event. This requires the ability to get the page number of each page in the PDF document and to be able to set the page number and to join the pages together. Copilot suggested using the PdfSharp library for this. This was becoming too complex. It was decided to make a direct call the Copilot to generate a method to print the whole Meet in one PDF document as per the previous methods.
Query: Can we now fully implement ExportMeetToPdf(): public static void ExportMeetToPdf(AthStitcherModel vm, string outputPdfPath, string? stitchedImagePath = null) { Meet meet = vm.CurrentMeet;
The method was generated:
/// <summary>
/// Export all heats for a given event into a single continuous PDF.
/// Each heat starts on a fresh page; table headers repeat inside each heat's table.
/// </summary>
public static void ExportMeetToPdf( ... )
{
Complete Code in Data/ToPdf.cs in ExportMeetToPdf()
Note this is the final version of the class file.`
There was then code for MainWindow.xaml.cs in PrintOneMeetAsPdf()) that calls ExportMeetToPdf().
There was an issue with respect to Heat results straddling a page break. Copilot suggested a solution which, after many iterations did not work. Looking elsewhere an alternative was proposed and was implemented by Copilot.
Query: With
ExportMeetToPdf(), can each Event start on a new page and any Heat not straddle a page break.
Later..
Query: Can we use
.PreventPageBreak()instead of.KeepTogether:
column.Item()
.PreventPageBreak()
.Text(text =>
…instead of:
content.Item().Element(containerHeat =>
{
containerHeat.KeepTogether(k =>
{
This worked 😄
Conclusion
The QuestPdf library was used to generate PDF documents from tabular data in a WPF app. GitHub Copilot was used to generate the code for the methods to generate the PDFs for Heats, Events and Meets. Some Copilot query iterations were required as well as tweaking of the generated code was required to fit the context of the AthStitcher app. At no stage was any major undertaking undertaken at this end to code against the QuestPdf library API. This was left the Copilot to generate but did require some iterations involving refinements of queries to get the desired results. In one case, the Meet Pdf, it was necessary to unwind and start again.
The desired outcome, generating Pdf documents from Sqlite DBMS tabular data front ended with Entity Framework was successful! 😄
Footnote 1
The Markdown code here links directly to specific methods in code pages in a specific project in a GitHub repository. This is done by searching for the method name in the relevant file path in the repository. This is useful for readers who want to see the complete context of the code snippets provided in the blog post without the need to provide full content here. A typical link thus is:
[Link text typically the method name](https://github.com/<Owner>/<Repository>/search?q=<Method Name>+path:<Project>/<Relative path to code page>&type=code))
For example:
[ExportMeetToPdf()](https://github.com/djaus2/PhotoTimingDjaus/search?q=ExportMeetToPdf+path:AthStitcher/Data/ToPdf.cs&type=code)
Working on a Markdown Macro for this ….
Footnote 2
GitHub Copilot was used in the Visual Studio context. It was found to be quite difficult to recap the transcript. A discussion about this:
Is there a way to get a complete transcript, preferable formatted, of a conversation in GitHub CoPilot Chat in Visual Studio?
I asked Copilot but none of its replies worked. It suggested, amongst other things, cntrl-A then cntrl-C but that only works on one segment of the chat.
Further:
You CAN select all or part of suggested code segment
[BUG]If you select s section before or after a code segment Cntrl-C does not copy. You have to right-click and select Copy even though it says Cntrl-c to copy there!
`
| Topic | Subtopic | |
| This Category Links | ||
| Category: | Application Dev Index: | Application Dev |
| < Prev: | CommunityToolkit.Mvvm | Further - Observable Entities |