PBToolboxAI v4 ← Site

messagebox — n_pbt_messagebox #

← Component reference · Guide contents

Themed modal dialog box with a synchronous return value: the drop-in replacement for PowerBuilder's MessageBox(), with rich text, free-form buttons, icons and a check box.

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


At a glance #

Objectn_pbt_messagebox — non-visual: nothing to place in the window
Used forAsking a question or announcing a result, instead of the rigid, unthemed native MessageBox()
Return valueSynchronous: of_show() blocks and returns the index of the button that was clicked

Unlike the visual components, this object is not dropped into a window: you create, configure, show, destroy it.

// Local variables
n_pbt_messagebox lnv_mb

// Create the dialog object
lnv_mb = create n_pbt_messagebox

// ... configuration ...

// Release the dialog object
destroy lnv_mb

Quick start #

// Local variables
n_pbt_messagebox lnv_mb

// Create the dialog object
lnv_mb = create n_pbt_messagebox

// Configure the box
lnv_mb.is_title   = "Deletion"
lnv_mb.is_icon    = lnv_mb.ICON_WARNING
lnv_mb.is_message = "Permanently delete [b]12 folders[/b] ?[br][br]This action cannot be undone."

// Add the buttons
lnv_mb.of_add_button(/*text*/ "Delete", /*default*/ true,  /*cancel*/ false)   // -> 1
lnv_mb.of_add_button(/*text*/ "Cancel",   /*default*/ false, /*cancel*/ true)    // -> 2

// Show it modal, then act on the first button
if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 1 then
    of_delete()
end if

// Release the dialog object
destroy lnv_mb

of_show waits for the user's answer: the next line only runs after the click, exactly as with MessageBox().


Properties #

To be set before of_show.

PropertyTypeDefaultPurpose
is_titlestring""Title displayed in the header of the box
is_messagestring""Body of the message. Accepts rich text markup ([b], [i], [br], [accent], [picture=…]…). The text of the message and of the instruction can be selected and copied. A value that comes from your data goes through of_escape_markup first: otherwise a bracket in it is read as markup — an [action=x] in a customer name would close the box
is_instructionstring""Main instruction: the question itself, shown bigger above the message. Title / instruction / message is the anatomy that makes a dialog readable at a glance — "Delete 42 rows?" then "This cannot be undone" — instead of one uniform block. Accepts the rich markup
is_iconstring""Icon: an ICON_* constant, or your own image (a file path, or a DLL resource my.dll:NAME)
is_checkboxstring""Text of an optional check box, "don't ask me again" style ("" = no check box)
ib_checkedbooleanfalseInitial state of the check box (the final state is read with of_checked())
ib_inputbooleanfalseAdds a themed input field (rename, reason, comment), so an application no longer needs a hand-built window that follows neither the theme nor the reading direction. Read it back with of_input_value() after of_show. The whole thing in one call: of_prompt
is_input_labelstring""Label above the field ("" = none). Needs ib_input
is_input_valuestring""Initial content of the field. It is selected when the dialog opens: typing replaces it, as in every rename dialog
is_input_placeholderstring""Hint shown while the field is empty. It is not a value: nothing is returned if the user types nothing
ib_input_passwordbooleanfalseMasks the characters typed
ib_input_requiredbooleanfalseThe default button stays disabled while the field is empty. Letting the user submit only to be told off afterwards serves nobody; the cancel button stays reachable. A countdown on that button (of_add_button_timed) does not click it while the field is empty: it runs out and the button waits for a hand. A button that is both default and cancel is out of reach too: Esc and Alt+F4 then close the box without a choice (0)
ib_buttons_reversebooleanfalseButton order: false = left to right in the order they were added; true = reversed
ib_movablebooleantrueCan the box be moved? It has no title bar — it paints its own card — so Windows has no grip on it: we give it one, the card drags the window except what already answers a click and the text of the message, which is selectable. True by default, because a modal covering the very thing you need to read before answering is a trap. Set it false for a box that must stay where it is
is_positionstringPOSITION_OWNERCentering: POSITION_OWNER (on the calling window) or POSITION_SCREEN (on the screen)
il_min_widthlong0Minimum width in pixels (0 = automatic, 320); never below 200
il_max_widthlong0Maximum width in pixels (0 = automatic): the text wraps within that limit; never below 200
il_max_heightlong0Maximum height in pixels (0 = automatic): beyond it, the body of the message scrolls instead of growing the window
il_accentlong-1Accent colour of this box: the default button and the checkbox take it, and the readable text colour is derived from it. -1 (the default) follows the application. An error in red, a success in green, without touching the theme

Constants #

ConstantValueUse
ICON_INFORMATION"information"Neutral information
ICON_WARNING"warning"Warning, risky action
ICON_ERROR"error"Failure, error
ICON_QUESTION"question"Closed question
ICON_SUCCESS"success"Confirmation of a success
ICON_NONE"none"No icon
POSITION_OWNER"owner"Centered on the calling window
POSITION_SCREEN"screen"Centered on the screen

Methods #

MethodPurpose
of_add_button (string as_text) → longAdds a plain button. Returns its index, starting at 1
of_add_button (string as_text, boolean ab_default, boolean ab_cancel) → longSame thing, marking the button as the default one (Enter) and/or the cancel one (Esc). Returns its index, starting at 1
of_add_button (string as_text, string as_icon, boolean ab_default, boolean ab_cancel) → longSame thing, with an icon on the button. Returns its index, starting at 1
of_add_button_timed (string as_text, boolean ab_default, boolean ab_cancel, long al_enable_secs, long al_click_secs) → longCountdown button: stays disabled for al_enable_secs seconds (with a visible counter), then clicks itself after al_click_secs seconds (0 = timer off). While a cancel button still counts down, neither Esc nor Alt+F4 closes the box: the delay is there to make the user read. Returns its index, starting at 1
of_count ( ) → longReturns how many buttons the dialog holds. They are addressed by rank — what of_add_button returns, what of_show gives back — so they carry no key: there is no of_keys_at and no of_has here
of_show (long al_hwnd) → longShows the modal box and returns the index of the button that was clicked (0 = closed with Esc or Alt+F4, with no cancel button). A negative value means that no dialog could be shown: -4, or -6 when the WebView2 runtime is missing — of_get_last_error() says why. When the owner window closes while the box is up (a timer, an event), the box goes with it and of_show returns 0
of_get_last_error ( ) → stringWhy the last dialog could not open — an empty string when it did. Read it after an of_show (or a shortcut such as of_info) that returned a negative value, or after an of_choose or of_prompt that returned an empty string
of_checked ( ) → booleanState of the check box as of the last of_show
of_input_value ( ) → stringText typed during the last of_show (empty when ib_input was off)
of_action ( ) → stringId of the [action=id] zone clicked inside the message, an empty string otherwise. Such a zone is a choice offered in the sentence itself: it closes the dialog and of_show returns 0. A [hyperlink=url] zone, on the other hand, opens in the browser and leaves the dialog up — the caller is blocked inside of_show, so a link cannot be an answer
of_info (long al_hwnd, string as_title, string as_message) → longOne-line dialog, the way MessageBox() is one: information icon and a single OK button, returns 1. The button labels come from the library's own translations (6 languages) instead of being written in each application — which is the whole reason these shortcuts exist (negative: no dialog shown, see of_get_last_error)
of_warning (long al_hwnd, string as_title, string as_message) → longWarning icon, one OK button. Returns 1 (negative: no dialog shown, see of_get_last_error)
of_error (long al_hwnd, string as_title, string as_message) → longError icon, one OK button. Returns 1 (negative: no dialog shown, see of_get_last_error)
of_success (long al_hwnd, string as_title, string as_message) → longSuccess icon, one OK button. Returns 1 (negative: no dialog shown, see of_get_last_error)
of_confirm (long al_hwnd, string as_title, string as_message) → longQuestion + OK / Cancel. Returns 1 = OK, 2 = Cancel, 0 = dismissed (negative: no dialog shown, see of_get_last_error)
of_yes_no (long al_hwnd, string as_title, string as_message) → longQuestion + Yes / No. Returns 1 = Yes, 2 = No, 0 = dismissed (negative: no dialog shown, see of_get_last_error)
of_yes_no_cancel (long al_hwnd, string as_title, string as_message) → longQuestion + Yes / No / Cancel. Returns 1, 2, 3, or 0 when dismissed (negative: no dialog shown, see of_get_last_error)
of_add_choice (string as_key, string as_title, string as_description) → longAdds a choice under the message — a title and a description, like the command links of a Windows task dialog. Clicking it closes the dialog and of_action() gives its key (of_show returns 0). Returns the rank of the choice (1 for the first), -5 on an empty key, a key already taken, or a key holding / or `` — nothing is added then
of_choose (long al_hwnd) → stringShows the choices with a single Cancel button (translated) and returns the key of the choice clicked, or an empty string when the user cancelled. The list scrolls when it outgrows the screen. An empty string too when no dialog could be shown: of_get_last_error then says why. Without any choice, nothing is shown: an empty string, and of_get_last_error says "no choice to show"
of_prompt (long al_hwnd, string as_title, string as_message, string as_default) → stringAsks for a value and returns what was typed, or an empty string when the user cancelled. To tell an empty answer from a cancel, use of_show + of_input_value instead. An empty string too when no dialog could be shown: of_get_last_error then says why
of_reset ( )Clears every property and the buttons that were added: the same instance starts over from scratch

A button label accepts rich text markup and the & mnemonic ("&Save" underlines the S and activates the button with Alt+S); && displays a literal ampersand.


The keyboard #

KeyEffect
EnterTriggers the button marked as default
EscTriggers the button marked as cancel; with no cancel button, closes the box and returns 0. Alt+F4 does the same. While the cancel button still counts down (of_add_button_timed), neither closes
Alt + letterTriggers the button whose label carries that mnemonic
TabMoves the focus from one button to the next
Ctrl + CCopies the dialog (title, instruction, message, choices with their description, checkbox with its state [x] or [ ], button labels) to the clipboard, like every Windows dialog — handy when an error has to be forwarded to support. With some text selected in the message, copies the selection only

When the box opens, no button shows a focus outline: this is intentional, and it is how modern Windows dialogs behave. The outline only appears after the first press on Tab, that is, once the user explicitly switches to the keyboard. Enter and Esc remain active from the very first second, even with no visible focus.


Examples #

Closed question with a default button #

// Local variables
n_pbt_messagebox lnv_mb
long ll_answer

// Create the dialog object
lnv_mb = create n_pbt_messagebox

// Configure the box
lnv_mb.is_title   = "Save changes"
lnv_mb.is_icon    = lnv_mb.ICON_QUESTION
lnv_mb.is_message = "The folder has been modified. Do you want to save before closing ?"

// Add the buttons
lnv_mb.of_add_button(/*text*/ "&Save",     /*default*/ true,  /*cancel*/ false)  // 1
lnv_mb.of_add_button(/*text*/ "Do &not save", /*default*/ false, /*cancel*/ false) // 2
lnv_mb.of_add_button(/*text*/ "Cancel",          /*default*/ false, /*cancel*/ true)   // 3

// Show it modal, then release the dialog object
ll_answer = lnv_mb.of_show(/*hwnd*/ Handle(this))
destroy lnv_mb

// Act on the button clicked (its rank, from 1)
choose case ll_answer
    case 1 ; of_save() ; Close(parent)
    case 2 ; Close(parent)
    case else ; // 3 or 0 : do not close
end choose

Rich message and icon #

// Configure the box
lnv_mb.is_title   = "Import complete"
lnv_mb.is_icon    = lnv_mb.ICON_SUCCESS
lnv_mb.is_message = "[b]1,240 rows[/b] imported.[br][br]" &
                  + "[accent]18 duplicates[/accent] were skipped."

// Add its button, then show the box
lnv_mb.of_add_button(/*text*/ "OK", /*default*/ true, /*cancel*/ false)
lnv_mb.of_show(/*hwnd*/ Handle(this))

A value from your data in the message #

The message is rich text: a bracket in it is a tag. A customer name, a label typed by a user goes through of_escape_markup (from n_pbt_utils) before it enters — otherwise "Smith [action=x]" would close the box like a choice.

// Local variables
n_pbt_utils lnv_utils

// The name comes from the database : escape it, it is shown as it is
lnv_mb.is_title   = "Delete customer"
lnv_mb.is_message = "Delete the customer [b]" + lnv_utils.of_escape_markup(/*text*/ ls_name) + "[/b] ?"

// Add the buttons, then show the box
lnv_mb.of_add_button(/*text*/ "Delete", /*default*/ false, /*cancel*/ false)
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ true, /*cancel*/ true)
lnv_mb.of_show(/*hwnd*/ Handle(this))

A "don't ask me again" check box #

// Local variables
n_pbt_messagebox lnv_mb

// Create the dialog object
lnv_mb = create n_pbt_messagebox

// Configure the box
lnv_mb.is_title    = "Deletion"
lnv_mb.is_icon     = lnv_mb.ICON_WARNING
lnv_mb.is_message  = "Delete the selected rows ? This action cannot be undone."
lnv_mb.is_checkbox = "Don't ask me again"
lnv_mb.ib_checked  = false

// Add the buttons
lnv_mb.of_add_button(/*text*/ "Delete", /*default*/ true,  /*cancel*/ false)
lnv_mb.of_add_button(/*text*/ "Cancel",   /*default*/ false, /*cancel*/ true)

// Show it modal, then act on the first button
if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 1 then
    of_delete()

    // Remember the user choice
    ib_confirm_delete = not lnv_mb.of_checked()
end if

// Release the dialog object
destroy lnv_mb

Countdown button #

// Configure the box
lnv_mb.is_title   = "Restart"
lnv_mb.is_icon    = lnv_mb.ICON_INFORMATION
lnv_mb.is_message = "The application will restart to apply the update."

// "Continue" stays grayed out for 3 seconds (a counter is displayed)
lnv_mb.of_add_button_timed(/*text*/ "Continue", /*default*/ true, /*cancel*/ false, /*enable_secs*/ 3, /*click_secs*/ 0)

// "Later" clicks itself after 10 seconds
lnv_mb.of_add_button_timed(/*text*/ "Later", /*default*/ false, /*cancel*/ true, /*enable_secs*/ 0, /*click_secs*/ 10)

// Show it modal
lnv_mb.of_show(/*hwnd*/ Handle(this))

Long message: capping the size #

// A large body of text : the box is capped and the body scrolls
lnv_mb.is_title      = "Release notes"
lnv_mb.is_message    = ls_notes
lnv_mb.il_max_width  = 480
lnv_mb.il_max_height = 320

// Add its button, then show the box
lnv_mb.of_add_button(/*text*/ "Close", /*default*/ true, /*cancel*/ true)
lnv_mb.of_show(/*hwnd*/ Handle(this))

Reusing an instance #

// One window instance, several dialogs : of_reset between each call
inv_mb.of_reset()      // clears the properties AND the previous buttons

// Configure the new box, then show it
inv_mb.is_title   = "Second dialog"
inv_mb.is_message = "Every of_reset starts over from a blank box."
inv_mb.of_add_button(/*text*/ "OK", /*default*/ true, /*cancel*/ false)
inv_mb.of_show(/*hwnd*/ Handle(this))

Best practices #


← Component reference · Guide contents