Our field service team needed offline reports generated from our .NET MAUI desktop app. The reports used XAML layouts—rich formatting, data grids, charts—that had to become PDFs for customer signatures.
IronPDF's MAUI extension converts ContentPages directly to PDF. Here's the implementation.
How Do I Convert MAUI Pages to PDF?
Install both IronPdf and IronPdf.Extensions.Maui packages:
Install-Package IronPdf
Install-Package IronPdf.Extensions.Maui
Then use RenderContentPageToPdf:
using IronPdf;
using IronPdf.Extensions.Maui;
// Install via NuGet: Install-Package IronPdf.Extensions.Maui
var renderer = new [ChromePdfRenderer](https://ironpdf.com/blog/videos/how-to-render-webgl-sites-to-pdf-in-csharp-ironpdf/)();
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
pdf.SaveAs("output.pdf");
This renders the current MainPage as a PDF with all XAML layouts preserved.
Does This Work on Mobile?
No. IronPDF does not support iOS or Android. It works on:
- Windows (desktop)
- macOS (desktop)
- Linux (desktop)
For mobile PDF generation, consider platform-specific libraries or server-side PDF creation with API calls.
Can I Render Pages Other Than MainPage?
Yes. Specify any ContentPage type:
var renderer = new ChromePdfRenderer();
// Render a custom report page
var pdf = await renderer.RenderContentPageToPdf<ReportPage, App>();
pdf.SaveAs("report.pdf");
The page must be a valid ContentPage in your MAUI application.
How Do I Add Headers and Footers?
Configure RenderingOptions before rendering:
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
HtmlFragment = "<h3 style='text-align:center'>Monthly Report</h3>"
};
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
HtmlFragment = "<div style='text-align:center'>Page {page} of {total-pages}</div>"
};
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
pdf.SaveAs("with-headers.pdf");
Headers and footers appear on every page.
Can I Style the PDF Output?
Yes. Apply CSS through RenderingOptions:
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 50;
renderer.RenderingOptions.MarginBottom = 50;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
pdf.SaveAs("styled.pdf");
This sets margins and paper size for the output PDF.
Does Data Binding Work?
Not yet. RenderContentPageToPdf currently doesn't support XAML data binding. Static content renders correctly, but bound properties may not appear.
Workaround: populate controls programmatically before rendering:
public partial class ReportPage : ContentPage
{
public ReportPage(ReportData data)
{
InitializeComponent();
// Manually set values instead of binding
TitleLabel.Text = data.Title;
DataGrid.ItemsSource = data.Items;
}
}
// Then render
var reportPage = new ReportPage(myData);
var pdf = await renderer.RenderContentPageToPdf<ReportPage, App>();
How Do I Trigger PDF Generation from a Button?
Wire up a button click handler:
In MainPage.xaml:
<Button Text="Generate PDF" Clicked="OnGeneratePdfClicked" />
In MainPage.xaml.cs:
private async void OnGeneratePdfClicked(object sender, EventArgs e)
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
var filePath = Path.Combine(FileSystem.AppDataDirectory, "report.pdf");
pdf.SaveAs(filePath);
await DisplayAlert("Success", $"PDF saved to {filePath}", "OK");
}
Users click the button to generate PDFs on demand.
Can I Include Images from XAML?
Yes. Images embedded in XAML render in the PDF:
<Image Source="logo.png" HeightRequest="100" />
Ensure images are included in your project as embedded resources or are accessible at runtime.
How Do I Handle Multi-Page Content?
Content that exceeds one page automatically flows to additional pages:
<ScrollView>
<StackLayout>
<Label Text="Page 1 content..." />
<!-- Lots of content -->
<Label Text="Page 2 content..." />
</StackLayout>
</ScrollView>
IronPDF paginates automatically based on content height and configured paper size.
What About Complex Layouts with Grids?
CollectionViews and data grids render correctly:
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Name}" />
<Label Grid.Column="1" Text="{Binding Value}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Note: As mentioned, data binding may not work. Populate ItemsSource programmatically before rendering.
Can I Generate PDFs Asynchronously?
Yes. RenderContentPageToPdf is async:
private async Task GenerateReportAsync()
{
var renderer = new ChromePdfRenderer();
// Runs without blocking UI
var pdf = await renderer.RenderContentPageToPdf<ReportPage, App>();
pdf.SaveAs("async-report.pdf");
}
This keeps the UI responsive during PDF generation.
How Do I Save PDFs to User-Selected Locations?
Use file pickers:
private async void OnSavePdfClicked(object sender, EventArgs e)
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
var result = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Save PDF"
});
if (result != null)
{
var stream = await result.OpenReadAsync();
pdf.Stream.CopyTo(stream);
}
}
Users choose where to save the PDF.
What's the Performance Like?
Simple pages: 500-1000ms
Complex pages with images: 1-3 seconds
Pages with large data tables: 3-5 seconds
For better performance, generate PDFs on background threads:
await Task.Run(async () =>
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderContentPageToPdf<ReportPage, App>();
pdf.SaveAs("background-report.pdf");
});
Can I Combine Multiple Pages into One PDF?
Render pages separately and merge:
var renderer = new ChromePdfRenderer();
var page1 = await renderer.RenderContentPageToPdf<CoverPage, App>();
var page2 = await renderer.RenderContentPageToPdf<DataPage, App>();
var page3 = await renderer.RenderContentPageToPdf<SummaryPage, App>();
var merged = PdfDocument.Merge(page1, page2, page3);
merged.SaveAs("complete-report.pdf");
This creates multi-section reports from different XAML pages.
What About Printing the PDF?
After generation, trigger the OS print dialog:
var pdf = await renderer.RenderContentPageToPdf<MainPage, App>();
// Save temporarily
var tempPath = Path.Combine(FileSystem.CacheDirectory, "temp.pdf");
pdf.SaveAs(tempPath);
// Open in default PDF viewer for printing
await Launcher.OpenAsync(new OpenFileRequest
{
File = new ReadOnlyFile(tempPath)
});
Users can print from their default PDF application.
Does This Work with Custom Fonts?
Yes, if fonts are embedded in your MAUI app:
<Label Text="Custom Font Text" FontFamily="CustomFont" />
Include fonts in MauiProgram.cs:
builder.ConfigureFonts(fonts =>
{
fonts.AddFont("CustomFont.ttf", "CustomFont");
});
Fonts render correctly in the PDF.
How Do I Test PDF Generation Locally?
Run your MAUI app on Windows or macOS and test the button:
- Click "Generate PDF"
- Check the output path in AppDataDirectory
- Open the PDF to verify layout
I use automated tests for batch PDF generation:
[Test]
public async Task GeneratesReportPdf()
{
var renderer = new ChromePdfRenderer();
var pdf = await renderer.RenderContentPageToPdf<ReportPage, App>();
Assert.Greater(pdf.PageCount, 0);
File.Exists("test-report.pdf");
}
Written by Jacob Mellor, CTO at Iron Software. Jacob created IronPDF and leads a team of 50+ engineers building .NET document processing libraries.
Top comments (0)