PBToolboxAI v5 ← Site

picture — u_pbt_picture #

← Component reference · Guide contents

Image display: every common format, framing modes, alignment, automatic graying and a counter badge.

▶ See it live — Demo application, Picture tile: the preview, the code behind it and this page, side by side.


At a glance #

Userobjectu_pbt_picture
Item class— (component without items)
Used forReplacing a PowerBuilder picture: modern formats (SVG, WebP, animated GIF), controlled framing, clickable image, automatic graying
Opt-in optionsib_zoomable · ib_clickable · ib_allow_drop · ib_auto_height · ib_track_mouse

Quick start #

// window open event
uo_image.is_source  = "img\logo.png"
uo_image.is_stretch = uo_image.STRETCH_UNIFORM   // fits without distorting
uo_image.is_align   = uo_image.ALIGN_CENTER
uo_image.is_tooltip = "Company logo"

Where the image comes from: is_source #

is_source accepts four forms, all interchangeable:

FormExampleUse
Local fileimg\logo.pngpng, jpg, jfif, bmp, gif, ico, svg, webp, avif — and tif, tiff, wmf, emf, rle, converted on the fly
Web addresshttps://…/logo.pngImage loaded from a server (https only: an http:// address is refused, see below)
Embedded datadata:image/png;base64,…Image already in memory, with no intermediate file
DLL resourceimg\packimages.dll:SAMPLEImage packaged in a resource DLL

An anim: prefix can be placed in front of a GIF to explicitly flag an animation (GIFs animate anyway).

When the same name exists under several resource types in a DLL, state the type: img\packimages.dll:PNG/SAMPLE, SVG/SAMPLE, ICON/APP, RCDATA/BLOB. See Images and icons for the mono: and tint: prefixes, which recolor a glyph according to the theme.

An http:// address is refused: the component's page is secure, and the engine would silently switch the image to https:// — on an intranet server without TLS it would then fail with a misleading message. The component therefore refuses it up front: error glyph and ue_error with REASON_INSECURE. For an image on an unencrypted server, download it first with n_pbt_restclient.of_download into a temporary folder, then show that path. Setting the same path again reads the file again: an image your application has just rewritten (photo, scan, crop) shows up, as with the native Picture. An image on disk is limited to 32 MB.

The formats of the native Picture that the engine does not read — TIFF (scans, faxes), WMF, EMF and RLE — are converted on the fly: an application brought over from PowerBuilder keeps its pictures. Blanks around a path are ignored: a CHAR column read by GetItemString arrives padded with spaces, and Windows ignores them too; is_source reads back as you wrote it. A recoloured picture (mono:, tint:) served by another site over https is drawn only when that server allows cross-origin requests (the Access-Control-Allow-Origin header); otherwise ue_error says so (REASON_FAILED). Files of the workstation, DLL resources and data: are not concerned.

A picture in memory — a photo stored in the database and read by SelectBlob — is shown by of_set_picture(blob), like SetPicture of the native Picture: no temporary file, no base64. The format is recognised from the bytes; ue_loaded or ue_error follow, as for is_source.


How it is framed: is_stretch #

ConstantEffect
STRETCH_UNIFORMDefault. The image is scaled without distortion and stays fully visible; margins may appear
STRETCH_UNIFORMTOFILLScaled without distortion, but the frame is filled completely; anything overflowing is cropped
STRETCH_FILLThe image is stretched to fit the frame exactly — it may be distorted
STRETCH_NONEOriginal size, no scaling

is_align decides the horizontal position of the image when it is smaller than the control: ALIGN_CENTER (default), ALIGN_START, ALIGN_END. The vertical axis has its own property, is_valign. The placement is read on screen: ALIGN_START stays on the leading edge whatever ii_rotation and ib_mirror. An unknown value reads back as center.


Constants #

ConstantValueFor
STRETCH_NONE · STRETCH_FILL · STRETCH_UNIFORM · STRETCH_UNIFORMTOFILL"none" "fill" "uniform" "uniformtofill"is_stretch
ALIGN_CENTER · ALIGN_START · ALIGN_END"center" "start" "end"is_align
VALIGN_TOP · VALIGN_CENTER · VALIGN_BOTTOM"top" "center" "bottom"is_valign
ROTATION_NONE · ROTATION_90 · ROTATION_180 · ROTATION_2700 90 180 270ii_rotation
REASON_NOT_FOUND · REASON_TOO_LARGE · REASON_INSECURE · REASON_UNSUPPORTED · REASON_FAILED"notfound" "toolarge" "insecure" "unsupported" "failed"ue_error (as_reason)

ALIGN_START and ALIGN_END are logical: they follow the writing direction (Language and RTL). The physical values left and right are still accepted as aliases.


Properties #

PropertyTypeDefaultPurpose
is_sourcestring""The image to display (see the four forms above)
is_stretchstring"uniform"Framing mode: STRETCH_NONE, STRETCH_FILL, STRETCH_UNIFORM, STRETCH_UNIFORMTOFILL
is_alignstring"center"Horizontal placement when the image is smaller than the control: ALIGN_CENTER, ALIGN_START, ALIGN_END. The vertical axis is is_valign
is_valignstring"center"Vertical placement when the image is smaller than the control: VALIGN_TOP, VALIGN_CENTER, VALIGN_BOTTOM. The two axes are independent — is_align gives the column, this one the row — which is what makes a corner reachable
ib_enabledbooleantrueWhen false, the image is displayed in grayscale and answers nothing: clicks, wheel, keyboard and file drops
ii_badgeinteger0Counter badge in the top corner at the end of the line: right in a left-to-right reading, left in a right-to-left one (0 = none). Beyond 99 it shows 99+; a screen reader hears it with the picture ("Mailbox, 5 new")
il_badge_colorlong-1Badge background, as a PowerBuilder RGB value (-1 = not set: the color that comes from the theme; 0 is black, like any other color). The text color is picked automatically so that the counter stays legible
ii_badge_sizeinteger0Badge height in pixels (0 = the size that comes from the theme). The font size follows on its own: the counter stays centered whatever the size
ib_track_mousebooleanfalseOpt-in: enables ue_mouse_enter / ue_mouse_leave
is_placeholderstring""Stand-in image, shown while is_source is empty (an « add a photo » frame, a silhouette). Displayed dimmed: a stand-in is not the content, and it is never announced as a loaded image. Accepts the mono: prefix, which recolours a monochrome glyph with the theme
is_error_sourcestring""Fallback image when the source fails to load. Leave it empty and the component shows its own error glyph — never an empty box, which tells the user nothing while ue_error only travels to your code. Accepts the mono: prefix
ii_rotationinteger0Quarter turns, for the scans and photos that arrive lying down: ROTATION_NONE, ROTATION_90, ROTATION_180, ROTATION_270 (anything else reads as 0). A quarter turn swaps the fitting axes as well, so the picture keeps its proportions instead of being squashed
ib_mirrorbooleanfalseHorizontal mirror on screen: the picture is flipped left to right, whatever ii_rotation
ib_zoomablebooleanfalseOpt-in: the user may come closer (wheel, towards the pointer, or the + and - keys), move around (drag, or the arrows — inverted in a right-to-left reading) and go back to the fitted view (double click, or the key marked 0, whatever the keyboard layout); the 1 key shows the picture at its ACTUAL size, one pixel of the picture for one pixel of the screen. With Ctrl or Alt held down, these keys are left to your application. Larger than its frame, the picture always covers it: a drag never uncovers an empty band; smaller, it stays where is_align and is_valign put it. Only the left button drags it. For a plan, a scan or a photo — where four fixed fitting modes are not enough
id_zoomdouble1.0Zoom factor: 1.0 is the framing chosen by is_stretch, up to id_max_zoom (8.0 by default). Reading it back gives the current factor, wheel and keyboard included (ue_zoom_changed tells you each change: a gesture of the user, this property set by your code, or a new source bringing it back to 1.0). Needs ib_zoomable, set first: without it the factor stays 1.0. Set it after is_source: a new picture starts at the fitted view
id_max_zoomdouble8.0The CEILING of the zoom, in the unit of id_zoom (1.0 = the fitted view): the wheel, the + key and id_zoom stop there, whatever the size of the picture. Any value from 1.0; under it, the default comes back. Only the 1 key goes beyond, to the ACTUAL size — a large scan shown small remains readable pixel for pixel; from there, + does nothing and - comes back down. Lowered under the current zoom, the zoom comes down to it and ue_zoom_changed says so
ib_allow_dropbooleanfalseOpt-in: accepts files dropped from the Windows Explorer. The frame shows it is armed, and the full paths arrive through ue_drop_files — loading them into is_source is up to your application. A disabled picture (ib_enabled = false) refuses every drop, and ib_allow_drop still reads back as set
ib_auto_heightbooleanfalseOpt-in: the userobject takes the height that keeps the image's proportions at its current width. For a picture that height is deducible: no need to compute it yourself from the dimensions reported by ue_loaded
is_alt_textstring""Text alternative: what a screen reader says of the picture, followed by the count of the badge, or by "Picture unavailable" when the source failed. Leave it empty for a purely decorative picture
ib_clickablebooleanfalseOpt-in: the picture is a BUTTON — in the Tab order, with a visible focus ring, and Enter or Space raise ue_clicked like a click (the window's Default button does not answer then). Disabled, it leaves the Tab order. A screen reader announces a button named by is_alt_text. Without it, a picture is an image: Enter goes on to the window
is_theme_stylestring""Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change
is_theme_modestring""Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change
il_theme_accentlong-1Accent colour of this component (-1 = the application accent, or the theme's)
is_tooltipstring""Simple tooltip shown when hovering the component
is_super_tooltip_titlestring""Title of the rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip

Methods #

MethodPurpose
of_reset ( )Resets every property to its default and removes the image. Returns 0 once applied, -2 when the component is not created
of_set_picture (blob ablb_image)Shows a picture held in memory — a photo read from the database by SelectBlob — like SetPicture of the native Picture: no temporary file, no base64. The format is recognised from the bytes (tif, wmf and emf converted); is_source then reads back the address the picture is served at, and ue_loaded or ue_error follow. Returns 0 once handed over, -5 when the blob is empty or not a picture, -4 beyond 32 MB, -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 #

EventRaised when
ue_clicked ( )Left click on the image — or Enter / Space with ib_clickable. Dragging the zoomed picture is not a click, and a double click raises only one, like the native Picture
ue_loaded (long al_width, long al_height)The image has loaded; the arguments carry its original size in pixels. For an SVG they are the dimensions it declares (width, height); an SVG drawn at 100 % declares none, and the engine gives it a default box (150 pixels high, the proportions of its drawing): the ratio is right, the size means nothing
ue_error (string as_message, string as_reason)The image could not be loaded. as_message names the source (image load failed : <source>), as_reason says why: REASON_NOT_FOUND (missing file), REASON_TOO_LARGE (over 32 MB), REASON_INSECURE (an http:// address, refused), REASON_UNSUPPORTED (a format the component does not show), REASON_FAILED (anything else: a file that does not decode, a server that does not answer)
ue_rclicked ( )Right click on the image
ue_double_clicked ( )Double click on the image. One ue_clicked comes before it, never two — like the native Picture. When ib_zoomable is on, the double click also brings the picture back to the fitted view: the event is raised either way, and what it means is up to you
ue_zoom_changed (double ad_zoom)The zoom factor changed: wheel, double click, the +, -, 0 and 1 keys, id_zoom or a lower id_max_zoom set by your code, a new is_source or ib_zoomable turned off (back to 1.0) — nothing when the factor stays the same, nor on of_reset. 1.0 means back to the framing of is_stretch
ue_auto_height (long al_height)The component settled on a new height; the userobject has already been resized when this fires. Needs ib_auto_height
ue_drop_files (string as_files[])Files dropped from the Explorer: full paths, one entry per file. Needs ib_allow_drop
ue_drag_enter ( )A file drag entered the component (ib_allow_drop)
ue_drag_leave ( )The file drag left the component
ue_mouse_enter ( )The mouse enters — requires ib_track_mouse = true
ue_mouse_leave ( )The mouse leaves — requires ib_track_mouse = true
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)

Examples #

Showing the photo on a record #

// rowfocuschanged event of dw_customer : show the photo of the current customer
// Local variables
string ls_customer_code

// The code of the customer on the new row
if currentrow < 1 then return
ls_customer_code = this.GetItemString(currentrow, "customer_code")

// The photo fills its frame ; the blanks padding a CHAR column are ignored
uo_photo.is_source  = "photos\" + ls_customer_code + ".jpg"
uo_photo.is_stretch = uo_photo.STRETCH_UNIFORMTOFILL
// Once, in the open event of the window : the picture shown when a photo is missing
uo_photo.is_error_source = "img\photo_missing.svg"
// ue_error event of uo_photo : (string as_message, string as_reason)
// The fallback picture already shows : here, only count the missing photos
if as_reason = uo_photo.REASON_NOT_FOUND then
    il_missing_photos = il_missing_photos + 1
end if

is_error_source is the right way to deal with a missing photo: there is no need to test whether the file exists before assigning it. Never reassign is_source from ue_error: if the replacement were missing in its turn, the failure would start the load again, endlessly.

Showing a photo stored in the database #

// Local variables
blob lb_photo

// The photo of the employee, stored in a column of the database
SELECTBLOB photo INTO :lb_photo FROM employee WHERE emp_id = 4152 USING SQLCA;

// Shown straight from memory : 0 = handed over, -5 = not a picture
if uo_photo.of_set_picture(/*image*/ lb_photo) <> 0 then
    uo_photo.is_source = ""
end if

A clickable image, button-style #

// The picture, its name for the keyboard and screen readers, and its tooltip
uo_avatar.is_source    = "photos\user.png"
uo_avatar.is_alt_text  = "My account"
uo_avatar.is_tooltip   = "My account"

// A button for the keyboard too : Tab reaches it, Enter or Space raise ue_clicked
uo_avatar.ib_clickable = true
// ue_clicked event of uo_avatar
of_open_my_account()

Counter and graying #

// Local variables
long ll_unread

// A notification badge on a mail icon
ll_unread = 7
uo_mail.is_source = "img\packimages.dll:SVG/MAIL"
uo_mail.ii_badge  = ll_unread       // 0 makes the badge disappear

// Red beyond a threshold, theme color otherwise (-1)
if ll_unread > 20 then
    uo_mail.il_badge_color = RGB(/*red*/ 200, /*green*/ 30, /*blue*/ 30)
else
    uo_mail.il_badge_color = -1
end if

// Feature unavailable : the image turns grayscale, with no second image to supply
uo_mail.ib_enabled = ib_mail_allowed

Graying is computed automatically: you do not have to supply a second "disabled" image.

Finding out the real size of the image #

// Local variables
string ls_chosen_file, ls_name

// Show the file the user has chosen
if GetFileOpenName("Picture", ls_chosen_file, ls_name, "jpg", "Pictures (*.jpg;*.png),*.jpg;*.png", /*initdir*/ "", OFN_EXPLORER + OFN_NOCHANGEDIR) = 1 then
    uo_preview.is_source = ls_chosen_file
end if
// ue_loaded event of uo_preview : (long al_width, long al_height)
uo_status.of_panel(/*key*/ "main").is_text = String(al_width) + " x " + String(al_height) + " px"

// An image smaller than the frame : do not enlarge it needlessly. The size of the picture
// is in PIXELS, the one of the userobject in PowerBuilder units : compare them in one unit
if al_width < UnitsToPixels(uo_preview.width, XUnitsToPixels!) and al_height < UnitsToPixels(uo_preview.height, YUnitsToPixels!) then
    uo_preview.is_stretch = uo_preview.STRETCH_NONE
end if

A full-width banner #

// The banner image, filling its strip
uo_banner.is_source  = "img\banniere.jpg"
uo_banner.is_stretch = uo_banner.STRETCH_UNIFORMTOFILL   // fills the whole strip, without distorting
uo_banner.is_valign   = uo_banner.VALIGN_TOP               // keeps the top of the image visible

Best practices #

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.

MembersRoleDetailed in
of_resetPut the component back to zero3.6 Resetting a component: of_reset()
of_register_shortcut · of_clear_shortcutsThe component's keyboard chords3.5 Keyboard shortcuts
of_is_created · of_is_ready · of_get_last_errorWhether it was born, whether it is ready, what failed3.7 Diagnostics
of_save_as_png · of_save_as_jpgExport the rendering as an image3.8 Exporting the rendering as an image
of_set_redrawGroup changes into a single repaint3.10 Best practices
of_preload_iconsIcons shown with no delayInstant display: of_icon
of_set_translationTranslate one of the component's labels5.2 Adapting a label: of_set_translation
of_focus_webviewGive the component the focus6.4 Keyboard and focus
of_print · of_print_to_pdfPrint, or write a PDF6.9 Printing
of_set_property · of_get_property · of_component_nameDriving a property by its name3.1 The property engine
is_accessible_name · is_accessible_descriptionThe name and description a screen reader says3.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.


← Component reference · Guide contents