Thanks for the detailed suggestion. The good news is that you can already do this today with the MessageDialog class, which supports up to three custom-caption buttons and tells you which one was pressed:
Var dlg As New MessageDialog
dlg.Title = "Save Changes?"
dlg.Message = "Do you want to save your changes before closing?"
dlg.Icon = MessageDialogIcon.Caution
dlg.ActionButtonCaption = "Save"
dlg.AlternateActionButtonCaption = "Don't Save"
dlg.CancelButtonCaption = "Cancel"
Var result As MessageDialogButton = dlg.Show()
If result = MessageDialogButton.Action Then
# Save the document
ElseIf result = MessageDialogButton.Alternate Then
# Discard the changes
Else
# Cancel — keep the document open
End If
On the desktop, Show() waits for the user to respond and returns MessageDialogButton.Action, .Alternate, or .Cancel; pressing Escape or closing the dialog counts as the Cancel button whenever one is present. The icon values are MessageDialogIcon.Note (information), .Caution (warning), .Stop (error), and .Question, and there's an Explanation property for secondary text under the main message. In web apps the same class exists with one difference: Show() returns immediately and the chosen button arrives in the Dismissed(button) event instead.
Full reference: MessageDialog.
You're right that the quick MessageBox(message) form is single-button only - it shows one OK button and returns Nothing. I've logged a feature to give MessageBox multi-button overloads that return the pressed button: feedback.objo.dev/feature/1694.