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 #
| Object | n_pbt_messagebox — non-visual: nothing to place in the window |
| Used for | Asking a question or announcing a result, instead of the rigid, unthemed native MessageBox() |
| Return value | Synchronous: 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 : Cancel is the default one, Enter deletes nothing
lnv_mb.of_add_button(/*text*/ "Delete", /*default*/ true, /*cancel*/ false) // -> 1
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ true, /*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.
| Property | Type | Default | Purpose |
|---|---|---|---|
is_title | string | "" | Title displayed in the header of the box |
is_message | string | "" | 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_instruction | string | "" | 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_icon | string | "" | Icon: an ICON_* constant, or your own image (a file path, or a DLL resource my.dll:NAME) |
is_details | string | "" | Details folded under the message behind a "Show details" button: a report, a stack trace, an SQL statement. Plain text in a monospace zone that scrolls — a bracket stays a bracket. Ctrl+C copies it even folded: it is what support asks for |
is_details_label | string | "" | Label of the button that unfolds is_details ("" = the translated "Show / Hide details") |
is_footer | string | "" | A footer note under the buttons: a help link, a reminder. Rich markup; a [hyperlink] in it opens the browser and leaves the box up |
is_footer_icon | string | "" | Small icon in front of the footer: an ICON_* constant or your own image ("" = none) |
is_checkbox | string | "" | Text of an optional check box, "don't ask me again" style ("" = no check box) |
ib_checked | boolean | false | Initial state of the check box (the final state is read with of_checked()) |
ib_input | boolean | false | Adds 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 — it honours the settings of the field set beforehand (label, mask, required, multi-line, length, pattern) |
is_input_label | string | "" | Label above the field ("" = none). Needs ib_input . It names the field for a screen reader, and a click on it gives the field the focus; rich markup accepted, an [action] zone in it answers like a choice |
is_input_value | string | "" | Initial content of the field. It is selected when the dialog opens: typing replaces it, as in every rename dialog |
is_input_placeholder | string | "" | Hint shown while the field is empty. It is not a value: nothing is returned if the user types nothing |
ib_input_password | boolean | false | Masks the characters typed . An eye at the end of the field reveals them — with the mouse, or Alt+F8 from the keyboard. The value is never copied by Ctrl+C nor offered to the password manager |
ib_input_required | boolean | false | The 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) . Spaces alone do not fill the field (the value comes back as typed) |
ib_input_multiline | boolean | false | The field takes several lines (a comment, an address): Enter starts a new line, Ctrl+Enter answers with the default button. A masked field stays on one line |
il_input_max_length | long | 0 | Most characters the field takes (0 = no limit); a longer initial value is cut |
is_input_pattern | string | "" | A regular expression the whole value must match ([0-9]{4}: four digits). While it does not, a message shows under the field and the default button stays out of reach. An empty field is left to ib_input_required |
is_input_error | string | "" | The message shown under a value is_input_pattern refuses ("" = the translated "This value is not valid.") |
ib_buttons_reverse | boolean | false | Button order: false = left to right in the order they were added; true = reversed |
ib_movable | boolean | true | Can 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 . A shortcut such as of_info keeps this setting |
ib_closable | boolean | false | Esc and Alt+F4 on a box without a cancel button. false follows Windows: a box with a single button closes through it (of_info returns 1), a box with several (Yes / No) has to be answered — neither key closes it. true closes it without a choice: of_show returns 0 |
ib_sound | boolean | true | The Windows sound of the icon when the box opens (information, warning, error, question). false = a silent box. A box without an icon, or with your own image, plays nothing |
is_position | string | POSITION_OWNER | Centering: POSITION_OWNER (on the calling window) or POSITION_SCREEN (on the screen) |
il_min_width | long | 0 | Minimum width in pixels (0 = automatic, 320); never below 200 |
il_max_width | long | 0 | Maximum width in pixels (0 = automatic): the text wraps within that limit; never below 200 |
il_max_height | long | 0 | Maximum height in pixels (0 = automatic): beyond it, the body of the message scrolls instead of growing the window |
il_accent | long | -1 | Accent 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 . A shortcut such as of_error keeps it: set it, then call the shortcut |
Constants #
| Constant | Value | Use |
|---|---|---|
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 |
BUTTONS_OK | "ok" | of_messagebox: OK |
BUTTONS_OK_CANCEL | "okcancel" | OK / Cancel |
BUTTONS_YES_NO | "yesno" | Yes / No |
BUTTONS_YES_NO_CANCEL | "yesnocancel" | Yes / No / Cancel |
BUTTONS_RETRY_CANCEL | "retrycancel" | Retry / Cancel |
BUTTONS_ABORT_RETRY_IGNORE | "abortretryignore" | Abort / Retry / Ignore |
Methods #
| Method | Purpose | |
|---|---|---|
of_add_button (string as_text) → long | Adds a plain button. Returns its index, starting at 1 | |
of_add_button (string as_text, boolean ab_default, boolean ab_cancel) → long | Same 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) → long | Same 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) → long | Countdown 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 ( ) → long | Returns 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) → long | Shows the modal box and returns the index of the button that was clicked. 0 = closed without a button: an [action] zone or a choice (of_action), or Esc / Alt+F4 with ib_closable. Without a cancel button, Esc and Alt+F4 answer through the only button of a one-button box, and do not close a box that has several — as Windows does. 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 ( ) → string | Why 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 ( ) → boolean | State of the check box as of the last of_show | |
of_input_value ( ) → string | Text typed during the last of_show (empty when ib_input was off) | |
of_action ( ) → string | Id of the [action=id] zone clicked — in the message, the title, the instruction, a label or the footer — or the key of the choice clicked; an empty string otherwise. Such a zone is a choice offered in the text 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) → long | One-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) Esc returns 1 too, as MessageBox(…, OK!) does. | |
of_warning (long al_hwnd, string as_title, string as_message) → long | Warning 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) → long | Error 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) → long | Success 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 { , long al_default }) → long | Question + OK / Cancel. Returns 1 = OK, 2 = Cancel (Esc too). al_default picks the default button: 2 puts Enter on Cancel, the safe default of a destructive action (-5 when out of range) (negative: no dialog shown, see of_get_last_error) | |
of_yes_no (long al_hwnd, string as_title, string as_message { , long al_default }) → long | Question + Yes / No. Returns 1 = Yes, 2 = No. Like a Windows Yes / No box, Esc and Alt+F4 do not close it: it has to be answered (ib_closable = true makes them return 0). al_default picks the default button (-5 when out of range) (negative: no dialog shown, see of_get_last_error) | |
of_yes_no_cancel (long al_hwnd, string as_title, string as_message { , long al_default }) → long | Question + Yes / No / Cancel. Returns 1, 2, 3 (Cancel, Esc too). al_default picks the default button (-5 when out of range) (negative: no dialog shown, see of_get_last_error) | |
of_retry_cancel (long al_hwnd, string as_title, string as_message) → long | Error + Retry / Cancel, Retry by default. Returns 1 = Retry, 2 = Cancel (Esc too) (negative: no dialog shown, see of_get_last_error) | |
of_messagebox (long al_hwnd, string as_title, string as_text { , icon ae_icon { , button ae_buttons { , long al_default } } }) → long | PowerBuilder's MessageBox(), argument for argument: put lnv_mb.of_messagebox(Handle(this), in front of a MessageBox( call and it compiles — Question!, YesNo! and the default button as you wrote them. Returns the number of the button pressed, as MessageBox() does (Abort 1 / Retry 2 / Ignore 3). The texts show as they are: a bracket stays a bracket, ~r~n breaks the line. Esc follows Windows: the Cancel button, the OK of a single button, nothing on Yes / No and Abort / Retry / Ignore. The labels are translated. -5 on a default button out of range (nothing is shown); another negative value: no dialog shown (of_get_last_error) | |
of_messagebox (long al_hwnd, string as_title, string as_text, string as_icon { , string as_buttons { , long al_default } }) → long | The same, with strings: an ICON_* constant (or your own image) and a BUTTONS_* constant. -5 on an unknown set of buttons. Returns the same button number (1 for the first). | |
of_add_choice (string as_key, string as_title, string as_description { , boolean ab_enabled }) → long | Adds 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 . With ab_enabled = false`, the choice shows greyed and cannot be picked (the arrows skip it) |
of_choose (long al_hwnd) → string | Shows 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" . An [action] zone of the texts answers like a choice: its id comes back | |
of_prompt (long al_hwnd, string as_title, string as_message, string as_default { , boolean ab_password, boolean ab_required }) → string | Asks 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 . The settings of the field set beforehand are honoured (is_input_label, ib_input_password, ib_input_multiline, is_input_pattern…); the overload masks (ab_password) and requires (ab_required) for that call only | |
of_reset ( ) → long | Clears every property, the buttons and choices that were added and the answer of the last dialog (of_checked, of_action, of_input_value, of_get_last_error): the same instance starts over from scratch. The shortcuts (of_info, of_confirm, of_prompt…) only replace the content: they keep the appearance (accent, position, moving, sizes, button order, ib_closable, ib_sound) and the settings of the field Returns 0 once done. |
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 #
| Key | Effect |
|---|---|
| Enter | Triggers the button marked as default — unless the focus is on another button or a choice: that one answers. In a multi-line field, Enter starts a new line and Ctrl+Enter answers |
| Esc | Triggers the button marked as cancel; without one, the only button of a one-button box (of_info returns 1), and nothing on a box with several buttons (Yes / No): it has to be answered, as on Windows — unless ib_closable = true, which closes it and returns 0. Alt+F4 does the same. While the cancel button still counts down (of_add_button_timed), neither closes |
| Alt + letter | Triggers the button whose label carries that mnemonic. When several carry it, the focus moves from one to the next without answering (as on Windows): Enter picks |
| Tab | Moves the focus from one element to the next: choices, field, "Details" button, check box, buttons |
| Ctrl + C | Copies the dialog (title, instruction, message, details, choices with their description, "label: value" of the field — never a password —, checkbox with its state [x] or [ ], footer, 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 |
| ↑ ↓ Home End PgUp PgDn | Walk the list of choices (a disabled choice is skipped); Enter picks the one that has the focus |
| Alt + F8 | Reveals or masks a password, from the field |
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 ¬ 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
string ls_name
// The name comes from the database (here written by hand)
ls_name = "Smith [action=x] & Co"
// 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, Cancel by default, 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 #
// Local variables
string ls_notes
// A large body of text : the box is capped and the body scrolls
ls_notes = Fill("Lorem ipsum dolor sit amet. ", 200)
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))
Migrating a MessageBox() call #
of_messagebox takes the arguments of MessageBox() in the same order, the window in front: a search-and-replace migrates a call, and the values returned are the same.
// Local variables
n_pbt_messagebox lnv_mb
long ll_answer
// Create the dialog object
lnv_mb = create n_pbt_messagebox
// The arguments of MessageBox, the window in front : No is the default button
ll_answer = lnv_mb.of_messagebox(/*hwnd*/ Handle(this), /*title*/ "Delete", /*text*/ "Delete the customer ?", /*icon*/ Question!, /*buttons*/ YesNo!, /*default*/ 2)
// Release the dialog object
destroy lnv_mb
// The same numbers as MessageBox : 1 = Yes, 2 = No
if ll_answer = 1 then
of_delete()
end if
A list of choices #
// Local variables
n_pbt_messagebox lnv_mb
string ls_format
// Create the dialog object
lnv_mb = create n_pbt_messagebox
// The question, then one choice per answer : a key, a title, a description
lnv_mb.is_title = "Export"
lnv_mb.is_instruction = "How should the report leave ?"
lnv_mb.of_add_choice(/*key*/ "pdf", /*title*/ "PDF document", /*description*/ "One file, ready to send.")
lnv_mb.of_add_choice(/*key*/ "xlsx", /*title*/ "Excel workbook", /*description*/ "Figures stay figures.")
// A choice the user may not take right now : greyed
lnv_mb.of_add_choice(/*key*/ "fax", /*title*/ "Fax", /*description*/ "No fax line is configured.", /*enabled*/ false)
// Show the choices with one Cancel button : the key comes back, "" when cancelled
ls_format = lnv_mb.of_choose(/*hwnd*/ Handle(this))
// Release the dialog object
destroy lnv_mb
// Act on the key
choose case ls_format
case "pdf"
of_export_pdf()
case "xlsx"
of_export_xlsx()
end choose
Asking for a value #
// Local variables
n_pbt_messagebox lnv_mb
string ls_name
long ll_answer
// Create the dialog object
lnv_mb = create n_pbt_messagebox
// One line : returns what was typed, "" when the user cancelled
ls_name = lnv_mb.of_prompt(/*hwnd*/ Handle(this), /*title*/ "Rename", /*message*/ "New name of the report :", /*default*/ "Sales 2026")
if ls_name <> "" then
of_rename(ls_name)
end if
// A field with a rule : four digits, or OK stays out of reach
lnv_mb.of_reset()
lnv_mb.is_title = "PIN"
lnv_mb.ib_input = true
lnv_mb.is_input_label = "PIN code"
lnv_mb.is_input_pattern = "[0-9]{4}"
lnv_mb.is_input_error = "Four digits, please."
lnv_mb.of_add_button(/*text*/ "OK", /*default*/ true, /*cancel*/ false)
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ false, /*cancel*/ true)
// of_show then of_input_value tell an empty answer from a cancel
ll_answer = lnv_mb.of_show(/*hwnd*/ Handle(this))
if ll_answer = 1 then
of_check_pin(lnv_mb.of_input_value())
end if
// Release the dialog object
destroy lnv_mb
An error report: instruction, details, footer #
// Local variables
n_pbt_messagebox lnv_mb
long ll_answer
// Create the dialog object
lnv_mb = create n_pbt_messagebox
// The short story : the question in big, its consequence below
lnv_mb.is_title = "Save failed"
lnv_mb.is_icon = n_pbt_messagebox.ICON_ERROR
lnv_mb.is_instruction = "The order could not be saved"
lnv_mb.is_message = "Another order already carries the number 4152."
// The long story, folded : plain text that support copies with Ctrl+C
lnv_mb.is_details = "SQLSTATE = 23000~r~nViolation of PRIMARY KEY constraint 'pk_order'."
// A help link under the buttons, and the box in red
lnv_mb.is_footer = "Read [hyperlink=https://example.com/help]what to do[/hyperlink]."
lnv_mb.il_accent = RGB(/*red*/ 196, /*green*/ 43, /*blue*/ 28)
// Add the buttons, then show the box
lnv_mb.of_add_button(/*text*/ "&Retry", /*default*/ true, /*cancel*/ false)
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ false, /*cancel*/ true)
ll_answer = lnv_mb.of_show(/*hwnd*/ Handle(this))
// A negative value : no dialog could be shown, the reason is kept
if ll_answer < 0 then
of_log(lnv_mb.of_get_last_error())
end if
// Release the dialog object
destroy lnv_mb
A choice inside the sentence #
// Local variables
n_pbt_messagebox lnv_mb
// Create the dialog object
lnv_mb = create n_pbt_messagebox
// An [action] zone is an answer offered in the text itself
lnv_mb.is_title = "Disk full"
lnv_mb.is_icon = n_pbt_messagebox.ICON_WARNING
lnv_mb.is_message = "The folder is full. [action=folder]Open the folder[/action] to make room."
lnv_mb.of_add_button(/*text*/ "Cancel", /*default*/ false, /*cancel*/ true)
// The zone closes the box : of_show returns 0 and of_action gives its id
if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 0 and lnv_mb.of_action() = "folder" then
of_open_folder()
end if
// Release the dialog object
destroy lnv_mb
Best practices #
- Always call
of_reset()before reconfiguring a reused instance: without it, the buttons of the previous dialog are added to the new ones. - Systematically mark one default button and one cancel button: keyboard users expect Enter and Esc to work.
- Test the positive button (
= 1), never the negative one:if of_yes_no(…) = 1 thendeletes on a "Yes" only. The box has no X; without a cancel button, Esc does not close a Yes / No box. Withib_closable, the value0(closed without a choice) is treated as a cancellation. - On a destructive action, put the default button on Cancel (
of_confirm(…, 2), or/*default*/ trueon Cancel): a reflex Enter deletes nothing. - A shortcut (
of_info,of_error…) keeps the appearance set before: a redil_accentthenof_errorgives an error in red. - Pass
Handle(this)(orHandle(parent)) as the calling window: the box centers on it and the modality applies to the right window. - Save red and
ICON_ERRORfor genuine errors; a routine confirmation deservesICON_QUESTION. - For information that needs no answer at all, prefer a non-blocking notification: see toaster.