pdfviewer — u_pbt_pdfviewer #
← Component reference · Guide contents
Built-in PDF viewer: displays a local document or one published on the web, straight inside your window, with pagination, zoom and printing.
▶ See it live — Demo application, PDF viewer tile: the preview, the code behind it and this page, side by side.
At a glance #
| Userobject | u_pbt_pdfviewer |
| Item class | — (component without items) |
| Used for | Displaying an invoice, a purchase order, a contract or a manual without launching an external application |
| Opt-in options | — |
The component replaces the classic "save the PDF to a temporary file, then call ShellExecute": the document stays inside your application, and the user never leaves the current screen.
Quick start #
// window open event : display a document stored on disk
uo_pdf.is_source = "C:\factures\FA-2026-0142.pdf"
// ue_load_completed event of uo_pdf : (string as_source)
uo_status.of_panel(/*key*/ "main").is_text = "Document displayed"
That is all: setting is_source is enough to load and display the document.
Properties #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_source | string | "" | Document to display: a file path (absolute, relative to the application or on the network; a # is part of the name), a file:/// address, a web address https://… served as application/pdf, or a data:application/pdf address. Setting the value triggers the load; setting "" (or blanks only) clears the viewer and keeps its settings. Anything else is refused and reported by ue_load_failed — http:// included, and an address carrying a user name and a password (https://name:secret@…). A PDF held in a blob is shown by of_load_blob. Reads back as you wrote it |
ii_page | integer | 0 | Page to open on, counted from 1 (0 = the document's first page). Set while a document is shown, it is that document's page: it loads again, open on it. Set while the viewer is empty, it applies to the next document; otherwise a new document opens at its first page. To open a document on a page, set is_source then ii_page: a single load. Every change reloads the document and raises ue_load_completed again: the reader only reads its page when a document loads; the same value set again does nothing. Write only: reading it back gives the last page you asked for, not the one on screen. The reader is the one built into the web engine, and it reports nothing. |
ii_zoom | integer | 0 | Zoom in percent, from 25 to 500 — the range of the reader's own buttons (0 = left to the viewer). A value outside that range is brought back into it, and reads back as applied. Setting a zoom cancels is_fit, which contradicts it. Write only, like ii_page: if the user zooms with the reader's own toolbar, this property does not follow. |
is_fit | string | "" | Fit mode: FIT_PAGE, FIT_WIDTH, FIT_HEIGHT, or "" for none. Cancels ii_zoom |
ib_viewer_toolbar | boolean | true | Shows the viewer's own toolbar (table of contents, page number, zoom, rotate, search, print, save). Hide it when your window carries those commands itself. The reader built into WebView2 has no Full screen command |
is_destination | string | "" | Opens the document on a named destination of the PDF — the target of a bookmark, written by the tool that produced the file. "" for none; a name the document does not have opens it at its first page. Kept for the next documents until you change it. Every change reloads the document on screen |
ib_allow_save | boolean | true | Offers the Save and Save as commands of the reader's toolbar and of its menu. At false, the document is shown without offering to save a copy. Each viewer keeps its own setting: another window's changes nothing here. It is not a protection: the file stays readable on the disk. Every change reloads the document on screen; set before is_source, the document loads only once |
ib_allow_print | boolean | true | Offers the Print command of the reader's toolbar and of its menu. of_print still prints: your application decides. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads only once |
ib_allow_search | boolean | true | Offers the Search command of the reader's toolbar. The search belongs to the user (Ctrl+F too): the reader offers no search driven by your application. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once |
ib_allow_rotate | boolean | true | Offers the Rotate command of the reader's toolbar. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once |
ib_allow_navigation_pane | boolean | true | Offers the Table of contents button of the reader's toolbar, top left: it opens the side panel with the bookmarks and the page thumbnails. At false, the button goes away. Whether the panel is open is the reader's business and the user's: your application offers the button or not. Each viewer keeps its own setting. Every change reloads the document on screen; set before is_source, the document loads once |
is_theme_style | string | "" | Visual style of the component's own surface: what the viewer shows while it holds no document (before the first one, after is_source = "" or a refused document). The PDF reader keeps its own look: it follows the application theme (PBT_SetDefaultTheme), light or dark only, because WebView2 gives all its PDF readers one colour scheme. Empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant of the component's own surface, seen while the viewer holds no document. The PDF reader does not follow it: its toolbar and margins take the light or dark of the application theme (PBT_SetDefaultTheme). Empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's). No visible effect here: neither the PDF reader nor the empty viewer draws with an accent. The value is kept and reads back as set |
Methods #
| Method | Purpose |
|---|---|
of_refresh ( ) | Reads the current document again (from disk or the network) without touching the settings: the way to show a file regenerated at the same path. The scrolling position is not kept: the reader starts again at ii_page. After a failure, tries the same document again. A refused document is judged again: ue_load_failed is raised again. Returns 0 once asked, -4 without a document (is_source empty), -2 if the component is not created |
of_load_blob (blob ablb_pdf) · of_load_blob (blob ablb_pdf, string as_name) | Shows a PDF held in a blob — read from the database (SELECTBLOB) or received from a service — without writing a file, whatever its size: the bytes stay in memory and are served to this viewer only. The second argument is the name that is_source and ue_load_completed / ue_load_failed give back (document.pdf without it). The blob shown before is released, this one is by the next document or of_reset. Returns 0 once asked (ue_load_completed follows), -5 when the blob is empty or is not a PDF, -4 when it is too large for this process (64 MB in 32 bits, 512 MB in 64 bits) — is_last_error says why —, -2 when the component is not created |
of_print ( ) · of_print (boolean) | Opens the print preview of the document — not of the page that frames it. Returns 0 once the preview is asked for, -4 when no document is on screen, -2 when the component is not created. The argument has no effect here: it is always the PDF reader's preview |
of_print_to_pdf (string) · of_print_to_pdf (string, boolean) | Returns -4 on this component, without writing anything: the printed page would only be the reader's frame, never the document. The document already is a PDF: copy the file of is_source |
of_reset ( ) | Clears the viewer and resets every property to its default. Returns 0 once applied, -2 when the component is not created |
of_set_redraw (boolean) | Groups a burst of changes into a single render. Returns 0 once applied, -2 when the component is not created |
of_save_as_png (string) · of_save_as_jpg (string) | Exports the rendering as an image. Returns 0 once the image is written, -4 when writing fails, -2 when the component is not created |
Events #
| Event | Raised when |
|---|---|
ue_load_completed (string as_source) | The document is on screen; as_source is is_source as you wrote it (for a blob, the name given to of_load_blob). Also raised by of_refresh and by every change of page, zoom, fit, viewer toolbar, destination or an ib_allow_* command — set them before is_source for a single load; the same value set again does not raise it. Never raised on a failure |
ue_load_failed (string as_source, string as_reason) | The document could not be shown; as_reason is one of the REASON_* constants below. The viewer stays empty |
ue_link_clicked (string as_url) | The user followed a link in the document. The viewer stays on the document: open as_url wherever you want (the system browser, a webbrowser…). as_url is the link's address as is (https://…, mailto:…); a relative link of a local document arrives as a disk path. A link to a local file of the workstation (file:///) in a PDF is not followed: the WebView2 PDF reader blocks it; no ue_link_clicked is raised |
ue_ready ( ) | The component has finished loading; everything sent beforehand has been replayed |
ue_runtime_missing ( ) | The WebView2 runtime is missing: the component stays empty |
ue_bg_color (long al_color) | The component has computed its theme background color; the userobject has already adopted it (backcolor) |
Why a document is not shown #
as_reason of ue_load_failed is one of these u_pbt_pdfviewer constants. A PDF is recognised this way: a local file has the .pdf extension and starts with the %PDF- signature; a remote document is served with the application/pdf type (the only one the engine hands to its reader); a data: address announces application/pdf.
| Constant | Value | Cause |
|---|---|---|
REASON_NOT_FOUND | "notfound" | Missing or unreadable file, address answering 404 |
REASON_NOT_PDF | "notpdf" | Not a PDF: local file without the %PDF- signature, remote answer that is not application/pdf (a file server answering application/octet-stream: download the document first, see the examples), data: or blob of another type |
REASON_TOO_LARGE | "toolarge" | File too large for this process (beyond 64 MB in 32-bit, 512 MB in 64-bit), or a data: address of more than 2 MB of characters (about 1.5 MB of PDF) |
REASON_INSECURE | "insecure" | http://: not supported, serve the document over https:// |
REASON_UNSUPPORTED | "unsupported" | Another kind of address (ftp:, blob:…), or an address carrying a user name and a password (https://name:secret@…) |
REASON_NETWORK | "network" | Server unreachable, unknown name, connection cut |
REASON_CERTIFICATE | "certificate" | The site's certificate is invalid, expired or revoked |
REASON_AUTH | "auth" | The site or the proxy asks for authentication: download the document with the restclient (token, credentials), then show it (see the examples) |
REASON_HTTP | "http" | Another server error (403, 500…) |
REASON_REFUSED | "refused" | The site refuses to be shown in a frame, or sends the PDF as a download — the file is not downloaded for all that |
REASON_FAILED | "failed" | Any other cause |
What the user can do, without a single line of code #
The viewer displays its built-in toolbar above the document. There is nothing for you to program: it is provided and localized by the system.
| Action | How |
|---|---|
| Pagination | Mouse wheel and scroll bar, or type the page number directly in the n / total counter |
| Zoom | + / − buttons, fit to page or fit to width |
| Search | The search button of the toolbar, in the text of the document |
| Printing | The printer button of the toolbar (can be hidden with ib_allow_print), or of_print from your code |
| Saving | The download button, to save a copy of the document (can be hidden with ib_allow_save) |
| Rotation | Page rotation from the toolbar menu |
| Keyboard | As soon as the component has the focus (Tab or of_focus_webview): PageDown, the arrows, Home / End scroll the document, without a click first |
The browser's shortcuts — Ctrl+F, Ctrl+P, Ctrl + wheel — are switched off in every component, this one included: use the buttons of the reader's toolbar.
What the reader does not say #
The reader is the one built into the web engine: nothing to install, printing, search and PDF forms included. In return, it reports nothing to your application: not the page on screen, not the number of pages, not the real zoom, not the selected text, and search cannot be driven from code: the reader ignores the open parameters that would highlight a text or open its side panel. It has no Full screen command, and it follows the light or dark theme of the application, never the component's. ii_page and ii_zoom say where to open the document, not where the user is. A password-protected PDF: the reader asks the user for the password itself, your application cannot give it. This is a deliberate choice; a scriptable rendering would need an external library.
Examples #
Opening a local document #
// Absolute path, or relative to the application directory
uo_pdf.is_source = "doc\conditions-generales.pdf"
Opening a document published on the web #
// A web address loads exactly like a local file
uo_pdf.is_source = "https://www.monsite.fr/tarifs/catalogue-2026.pdf"
An internet connection is of course required; the load is asynchronous, and ue_load_completed tells you when it is done.
Displaying the PDF a DataWindow has just produced #
// Local variables
string ls_file
// One file per day, in the temporary folder
ls_file = "C:\temp\report_" + String(Today(), "yyyymmdd") + ".pdf"
// The DataWindow produces the file...
dw_report.SaveAs(ls_file, PDF!, false)
// ...and the viewer displays it immediately
uo_pdf.is_source = ls_file
Refreshing after the file has been regenerated #
// The file was rewritten in the same location : reload without touching is_source
uo_pdf.of_refresh()
Chaining several documents in the same viewer #
// rowfocuschanged event of dw_list : (long currentrow)
// Local variables
string ls_pdf
// The path of the PDF of the current row
ls_pdf = dw_list.GetItemString(currentrow, "pdf_path")
// No attachment empties the viewer (its settings kept) ; otherwise it shows it
if ls_pdf = "" then
uo_pdf.is_source = ""
else
uo_pdf.is_source = ls_pdf
end if
Tracking the end of the load #
// ue_load_completed event of uo_pdf : (string as_source)
uo_wait.Hide()
// of_print prints the document on screen
uo_print_button.ib_enabled = true
Saying why the document is not there #
// ue_load_failed event of uo_pdf : (string as_source, string as_reason)
uo_wait.Hide()
choose case as_reason
case uo_pdf.REASON_NOT_FOUND
uo_status.of_panel(/*key*/ "main").is_text = "Document not found: " + as_source
case uo_pdf.REASON_NOT_PDF
uo_status.of_panel(/*key*/ "main").is_text = "This file is not a PDF"
case else
uo_status.of_panel(/*key*/ "main").is_text = "Document unavailable (" + as_reason + ")"
end choose
Opening a link of the document elsewhere #
// ue_link_clicked event of uo_pdf : (string as_url)
// The viewer stays on the document : the link opens in the window's browser
uo_web.is_address = as_url
Printing the document #
// clicked of the Print button : the PDF reader's preview, on the document itself
if uo_pdf.of_print() = -4 then
uo_status.of_panel(/*key*/ "main").is_text = "No document to print"
end if
Showing without letting save or print #
// A confidential document : neither Save nor Print in the reader's bar
uo_pdf.ib_allow_save = false
uo_pdf.ib_allow_print = false
uo_pdf.is_source = is_current_document
Set them before is_source: the document loads only once. Each viewer keeps its own setting. It is not a protection: the file stays readable on the disk, and of_print still prints.
Preview in a tab, next to data entry #
// open event : the viewer takes one tab page, data entry the other
uo_tab.of_add_page(/*key*/ "entry", /*title*/ "Entry", /*page*/ uo_page_entry)
uo_tab.of_add_page(/*key*/ "preview", /*title*/ "Preview", /*page*/ uo_page_preview)
// The viewer sits inside uo_page_preview like any other control
uo_pdf.is_source = is_current_document
The component is hosted with no special precautions inside a tab or a dockcontainer panel.
Checking the file before displaying it #
// Local variables
string ls_path
// The file of the invoice on screen
ls_path = "C:\factures\" + is_number + ".pdf"
// No file, nothing to show : clear the viewer rather than leave the previous document
if not FileExists(ls_path) then
uo_pdf.is_source = ""
uo_status.of_panel(/*key*/ "main").is_text = "Invoice not found"
return
end if
// Otherwise, the invoice is shown
uo_pdf.is_source = ls_path
Showing a PDF read from the database (blob) #
// Local variables
blob lblb_pdf
// The invoice stored in the database, read into a blob
SELECTBLOB pdf_file INTO :lblb_pdf FROM invoice WHERE invoice_no = :is_number USING SQLCA;
// Shown straight from memory : no temporary file, whatever its size
if SQLCA.SQLCode = 0 then
uo_pdf.of_load_blob(/*pdf*/ lblb_pdf, /*name*/ "invoice-" + is_number + ".pdf")
end if
The name given comes back in is_source and in ue_load_completed; the blob is released by the next document or by of_reset.
Opening on a chapter #
// Opened on the chapter "prices" of the catalogue; the reader's Table of contents button lists the bookmarks
uo_pdf.is_destination = "prices"
uo_pdf.is_source = "doc\catalogue-2026.pdf"
The name of a destination is the one written by the tool that produced the PDF: your document generator knows it.
A document behind a token, or served as octet-stream #
// Local variables
long ll_status
string ls_file
// The restclient (inv_rest, set up when the window opened) sends the token and downloads the file
ls_file = "C:\temp\invoice-4152.pdf"
ll_status = inv_rest.of_download(/*url*/ "/orders/4152/invoice.pdf", /*path*/ ls_file)
// Then the viewer shows the file, whatever type the server announced
if ll_status = 200 then
uo_pdf.is_source = ls_file
end if
The engine's reader can neither send a token nor show an application/octet-stream answer (REASON_AUTH, REASON_NOT_PDF): the restclient does it for it.
Accepted formats and paths #
Form of is_source | Example | Note |
|---|---|---|
| Absolute path | "C:\docs\contrat.pdf" | The most reliable |
| Relative path | "doc\notice.pdf" | Relative to the application directory |
| Network path | "\\serveur\partage\bon.pdf" | The user must have read permissions |
Name with # | "C:\devis\Devis #12.pdf" | The # is part of the file name |
file: address | "file:///C:/docs/contrat.pdf" | Converted to a path, like an absolute path; file://localhost/C:/… and the four-slash UNC form file:////server/share/… too |
| Web address | "https://…/catalogue.pdf" | Served as application/pdf, asynchronous load. A #page=… written in the address is ignored: use ii_page. An address carrying credentials (https://name:secret@…) is refused (REASON_UNSUPPORTED): go through the restclient |
| In-memory document | "data:application/pdf;base64,…" | Nothing is written to disk. Beyond 2 MB of characters (about 1.5 MB of PDF), ue_load_failed with REASON_TOO_LARGE: for a larger document held in memory, of_load_blob |
http:// address | "http://intranet/bon.pdf" | Not supported: ue_load_failed with REASON_INSECURE. Serve the document over https:// |
| Empty | "" | Clears the viewer and keeps its settings; a value made of blanks too |
This component only supports PDF: anything else is refused and reported by ue_load_failed (REASON_NOT_PDF). For an image, use picture; for an HTML page, webbrowser.
Best practices #
- Watch
ue_load_failed: an invalid path, a file that is not a PDF or an unreachable site is reported there with its reason, and the viewer stays empty. - To clear the viewer (moving to a row without an attachment), set
is_source = "": otherwise the previous document stays visible.of_reset()clears it too, but also puts every setting back to its default — Save and Print included. of_refresh()is the way to show a file regenerated at the same path: it reads the file again without touching the settings. It does not keep the scrolling position — the reader starts again atii_page.- Every change of
ii_zoom,is_fit,ib_viewer_toolbar,is_destination,ib_allow_saveorib_allow_printreloads the document (the reader only reads its settings when a document loads) and raisesue_load_completedagain: set them beforeis_sourcefor a single load. The same value set again reloads nothing.ii_pageset while a document is shown applies to that document: to open another one on a page, setis_sourcethenii_page. - Provide a waiting indicator for remote or large documents, and hide it on
ue_load_completedand onue_load_failed. - Give the component a comfortable area (at least half the window): the built-in toolbar and the document both need room to stay readable.
- To display a web page rather than a PDF, use webbrowser; for an image, picture.
Inherited from the common base #
These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.
| Members | Role | Detailed in |
|---|---|---|
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
ib_created · ib_ready · is_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · is_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.4 Optional properties and events |
Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.