2020-05-10

Messagebox using the simple Vista Task dialog

Here is an updated function that I've been using for my MessageBox dialogs, using the Win32 TaskDialog function.
It brings some few improvements to the previous Messagebox extended function that I posted here several years ago:

- up to 6 buttons allowed
- use the default Windows icons, those stored in "IMAGERES.DLL", used by Windows allover
- a wider dialog window, that stretches to fit all your text
- apart from the WindowTitle and the Contents, you have the "Main Instruction" parameter
- introduced the timeout property
- allows using Unicodes, for some cool characters and Multilanguages

The resulting function is derived from the excellent sample published in VFPX , by Anatolyi Mogylevets and Tore Bleken - How to display a Task Dialog (Vista). The original sample displays the dialog, but using only the default Windows dialog buttons, such as "Ok, Cancel, Abort, Retry, Yes, No".


This function allows you to customize the captions of your buttons as well. As I did in the "Extended Messagebox" sample, I used BINDEVENT to bind to the WM_ACTIVATE Windows event to make the desired changes in the dialog before it is shown.
The usage is very simple:


Notice that the last parameter does the trick. The Timeout parameter can be a numeric value or a Character, with the time in milisseconds, and the string that will come together with the time shown. The tag will be replaced by the time in seconds, with the small unicode clock, like in the image below.

These dialogs are Unicode friendly. That means that we can add almost whatever characters we desire. Just search the Web for "UNICODE + Heart", and you'll get the codes for you to try.
For unicodes, it's important to notice that this works with UTF-16 (16 bit Unicode). For Double-byte characters, like the "Thumbs up", pass the 8 digits inside the tag. All unicodes are accepted, you can pass several unicodes separated by a single SPACE.


Function:
  • NEWDIALOG(tcTitle, tcMainInstruction, tcContent, tcnIcon, tcButtons, tnDefault, tnTimeout)

Parameters:
  • tcTitle - string to be used for the task dialog title.
  • tcMainInstruction - string to be used for the main instruction.
  • tcContent - string used for additional text that appears below the main instruction, in a smaller font. 
  • tcnIcon - Character or Integer that iidentifies the icon to display in the task dialog. This parameter can be an integer or some predefined text values. If this parameter EMPTY() or omitted, no icon will be displayed. The variety of icons is HUGE, almost all icons stored in the %systemroot%\system32\imageres.dll file. The imageres.dll file contains many icons, used almost everywhere in Windows 10. It has icons for different types of folders, hardware devices, peripherals, actions, and so on. Below is a list of the available strings and enumerated icons. Here you can also determine the background color of the dialog Main instruction. Send a string comma separated, having the desired main icon first, and after the comma a letter representing the background color: R=Red; G=Green; Y=Yellow; S=Silver; B=Blue; Empty() no background, and finally "-" means no left margin.
  • tcButtons - Specifies the push buttons displayed in the dialog box. A single string containing comma separated captions. If you wish to show a disabled button, add a "\" before the caption.
  • tnDefault - Specifies the button Id that will be focused. Default = 1
  • tnTimeout - Specifies the number of milliseconds the dialog will be displayed without input from the keyboard or the mouse before clearing itself. You can specify any valid timeout value. A value of less than 1 never times out until user enters input and behaves the same as omitting the nTimeout parameter. 
Returns:
  • nId - the Id of the selected button, or 0 (zero) if Cancelled or -1 for timed out




Sample 1:

? NewDialog("Vista Task Dialog", "The main instruction text for the TaskDialog goes here", ;
 "The content text for the task dialog is shown here and the text will automatically wrap as needed." + CHR(13) + CHR(13) + ;
 "Any expanded content text for the task dialog is shown here and the text will automatically wrap as needed." + CHR(13) + CHR(13) + ;
    "Do you like it?", "OK2", ;
 "Yes!,Not really,I dunno")

Sample 2:

? NewDialog("Bad Username or Password", ;
      "Access Denied", ;
      "You have entered the wrong credentials for 3 times." + CHR(13) + CHR(13) + ;
             "Please wait for 30 minutes and try again, otherwise your account may be suspended.", ;
      "Lock", ;
      "Return")

Sample 3:

? NewDialog("Critical error", ;
     "Corrupted Data", ;
     "An unexpected error has occurred and the system needs to be restarted." + ;
         CHR(13) + CHR(13) + "What do you want to do ?", ;
     "X2", ;
     "Restart Now,Restart later,Keep working", ; && Button captions
     2) && Default button


Sample 4: Dialogs using simple timeout, with unicodes - everywhere, titles, contents and even buttons!

? NewDialog("Nice job!  <UC>2661 2665 2764</UC>", ;
    "Success! <UC>d83ddc4d</UC>", ;
    "Your account details have been updated successfully." + ;
    CHR(13) + CHR(13) + "You can proceed with the next step to get your cashback!", ;
    "OK2", ;
    "Ok*,Ok <UC>2714</UC>,Ok <UC>D83DDDF9</UC>", ; && Button captions
    1, ;  && Default button
    9000) && Timeout



Sample 5: Unicodes - Multilanguages

Unicode friendly means endless usages, really very easilly. The dialog below uses several languages from different character sets.
To get the Unicodes, first I used Google translator to translate the word "Welcome". The results were pasted to one among many Unicode translators, like Branah - https://www.branah.com/unicode-converter. Just paste your text and it will provides the unicodes. Make sure to check the "Remove \u", and when pasting, make sure to separate every character with a space. In general, for most languages you'll use 4 characters. For double byte languages, like Chinese, Japanese and Korean, or for some special characters, like the "thumbs up", you'll pass every 8 characters at a time.


? NewDialog("Nice job!  <UC>2661 2665 2764</UC>", ;
    "Success! <UC>d83ddc4d</UC>", ;
    "Your account details have been updated successfully." + ;
    CHR(13) + CHR(13) + "You can proceed with the next step to get your cashback!", ;
    "OK2", ;
    "Ok*,Ok <UC>2714</UC>,Ok <UC>D83DDDF9</UC>", ; && Button captions
    1, ;  && Default button
    9000) && Timeout





Sample 6: Timeout with special caption

? NewDialog("Covid-19 crazy warning - See the timer -->", ;
    "Please stay home!", ;
    " - Clean your hands often." + CHR(13) + ;
    " - Keep a safe distance from anyone who is coughing or sneezing." + CHR(13) + ;
    " - Don’t touch your eyes, nose or mouth." + CHR(13) + ;
    " - Cover your nose and mouth with your bent elbow or a tissue when you cough or sneeze." + CHR(13) + ;
    " - Avoid close contact with people who are sick." + CHR(13) + ;
    " - Stay at home as much as possible." + CHR(13) + ;
    " - Put distance between yourself and other people." + CHR(13) + ;
    " - If you have a fever, cough and difficulty breathing, seek medical attention." + CHR(13), ;
    "!2", ;
    "More Info,I agree,Leave me!", ; && Button captions
    2, ;  && Default button
    "8000,<SECS> secs.") && Timeout








Sample 7: Predefined unicode buttons:
For the basic captions: "Ok, Cancel, Print, Save and Search", add an "*", and the corresponding unicode icon will be added to the button, as below:


? NewDialog("Playing with Unicodes in buttons", ;
    "Predefined buttons", ;
 "There are currently 5 predefined buttons that will add a unicode icon automatically." + CHR(13) + CHR(13) + ;
 "Add an asterisk - '*' after the words below, and the corresponding icons will be added to the buttons:" + CHR(13) + CHR(13) + ;
 "     Ok*        <UC>27f6</UC>     Ok <UC>2713</UC>" + CHR(13) + ;
 "     Cancel*    <UC>27f6</UC>     Cancel <UC>d83dddd9</UC>" + CHR(13) + ;
 "     Print*     <UC>27f6</UC>     Print <UC>2399</UC>" + CHR(13) + ;
 "     Save*      <UC>27f6</UC>     Save <UC>d83dddab</UC>" + CHR(13) + ;
 "     Search*    <UC>27f6</UC>     Search <UC>d83ddd0e</UC>", ;
    "I", ;
    "Ok*,Cancel*,Print*,Save*,Search*")  && Button captions




Sample 8: Custom title with no icon, and with background:
Notice the icon parameter - the comma separated string - ",S" means that there will be no main icon, but with a silver background:

* Sample 8
? NewDialog("Covid-19 warning", ;
    "Custom title with no icon and background" + CHR(13) + "PLEASE STAY HOME!" + CHR(13) + "I hope you'll keep your word!", ;
    " - Clean your hands often." + CHR(13) + ;
    " - Avoid close contact with people who are sick." + CHR(13) + ;
    " - Stay at home as much as possible." + CHR(13) + ;
    " - Put distance between yourself and other people." + CHR(13) + ;
    " - If you have a fever, cough and difficulty breathing, seek medical attention." + CHR(13), ;
    ",S", ; && No icon, silver background
    "\More Info,I agree,Leave me!") && Button captions, 1st button disabled



Sample 9: Buttons with custom icons, from Imageres.dll and Shell32.dll
Notice the index numbers in the button captions parameters. These come immediately after every caption.

* Sample 9
? NewDialog("Covid-19 crazy warning - See the timer -->", ;
    "Please stay home!", ;
    " - Clean your hands often." + CHR(13) + ;
    " - Avoid close contact with people who are sick." + CHR(13) + ;
    " - Stay at home as much as possible." + CHR(13) + ;
    " - Put distance between yourself and other people." + CHR(13) + ;
    " - If you have a fever, cough and difficulty breathing, seek medical attention." + CHR(13), ;
    "!2", ; && Exlamation default with yellow backgound (default)
    "\More Info_99,I agree_5341,Leave me!_89,Ok_116802", ; && Button captions, 1st button disabled
    2, ;  && Default button
    "8000,<SECS> secs.") && Timeout



Sample 10: Predefined buttons icons:
The icon parameter has the special characters: "I,B", meaning the Information icon, and the blue background
For the basic captions: "Ok, Cancel, Print, Save and Search", add an "#", and the corresponding unicode icon will be added to the button, as below:

* Sample 10
? NewDialog("Playing with real icons in buttons", ;
    "Custom title icon and background!" + CHR(13) + "Predefined buttons with colored icons", ;
 "There are currently 5 predefined buttons that will add some colored icons automatically." + CHR(13) + CHR(13) + ;
 "Add an HASHTAG - '#' after the words below, and the corresponding icons will be added to the buttons:" + CHR(13) + CHR(13) + ;
 "  -  Ok#     " + CHR(13) + ;
 "  -  Cancel# " + CHR(13) + ;
 "  -  Print#  " + CHR(13) + ;
 "  -  Save#   " + CHR(13) + ;
 "  -  Search# ", ;
    "I,B", ; && Information icon, blue background
    "Ok#,Cancel#,Print#,Save#,Search#")  && Button captions



Sample 11: Green background with custom icon, and unicodes

* Sample 11
? NewDialog("Playing with Unicodes in buttons", ;
    "You did it!!!" + CHR(13) + "Custom title icon and background!" + CHR(13) + "Predefined unicode buttons", ;
 "There are currently 5 predefined buttons that will add a unicode icon automatically." + CHR(13) + CHR(13) + ;
 "Add an asterisk - '*' after the words below, and the corresponding icons will be added to the buttons:" + CHR(13) + CHR(13) + ;
 "     Ok*        <UC>27f6</UC>     Ok <UC>2713</UC>" + CHR(13) + ;
 "     Cancel*    <UC>27f6</UC>     Cancel <UC>d83dddd9</UC>" + CHR(13) + ;
 "     Print*     <UC>27f6</UC>     Print <UC>2399</UC>" + CHR(13) + ;
 "     Save*      <UC>27f6</UC>     Save <UC>d83dddab</UC>" + CHR(13) + ;
 "     Search*    <UC>27f6</UC>     Search <UC>d83ddd0e</UC>", ;
    "Ok3,G", ; && Ok icon, green background
    "Ok*,Cancel*,Print*,Save*,Search*")  && Button captions







APPENDIX 1:
The image below shows all the icons stored in ImageRes.Dll. Each icon comes with an ID. Vhoose your button, and pass its ID number to the "tcnIcon" parameter to have it displayed in your dialog.






APPENDIX 2:
Some of the available strings and values for the tnIcon parameter:
ComputerNumericIconBackgroundImage
"!"-1 or 107Warning shield - !
"BEEP"
"!2"-6Warning shield - !Yellow
"!3"84Warning - !
"!4"1403Warning - !
"X"-2 or 105Error shield - X
"BEEP"
"X2"-7Error shield - XRed
"X3"89Error - X
"X4"98Error - X
"X5"1402Error - X
"I"-3Information - i
"BEEP"
"I2"81Information - i
"?"0x7F02 or 99Question - ?
"?2"104Question shield - ?
"OK"106Success shield - Ok
"OK2"-8Success shield - OkGreen
"OK3"1400Success
"OK4"1405Success
"SHIELD"-4Shield
.....
"KEY"82Key
"KEY2"5360Key
"LOCK"59Lock
"LOCK2"1304Lock
"LOCK3"5381Lock
"ZIP"174Zip
"SEARCH"177Search
"SEARCH2"5332Search
"USER"1029User
"USER2"5356User
"CLOUD"1043Cloud
"CLOUD2"1404Cloud
"STAR"1024Star
"FOLDER"1023Folder
"MAIL"20Mail
"CONNECT"25Connect
"CONNECT2"179Connect
"PRINTER"51Printer
"PRINTER2"45Printer
"CAMERA"57Camera
"FILM"46Film
"FAX"76Fax
"DOCUMENT"90Document
"SCAN"95Scan
"COMPUTER"109Computer
"COMPUTER2"149Computer
"DIAGNOSE"150Diagnose
"MUSIC"1026Music
"CANCEL"1027Cancel
"WRITE"5306Write
"PLAY"5341Play
"CLOCK"5368Clock
"MOBILE"6400Mobile






* UPDATE * 2020-05-10
First of all, thanks very much for the positive feedback, bug reports and suggestions from my friends from Foxite
I applied several fixes and tweaks - the most important were:

New property:
- tnDefault, determines the default button that will receive the focus

Fixes:
- When "ESC" or the dialog closed by clicking at the "X" button made the function wrongly return the value "2"
- Sometimes the returned values were negative


They seem to be very simple, but were really very tricky to fix.
The TaskDialog API does not allow almost any external interference, and this obliged me to test several aproaches to try to bypass it. Hopefully things will run fine now.

The most annoying difficulty was with the fact that I could not resize the buttons. This obliged me to use the original "Cancel" button, that had its caption renamed. But whenever the user clicked on the "X" or pressed <ESC> , the Dialog interpreted that the button that originally was named as "Cancel" was pressed. I had to control the WM_KEYUP Windows event, detect if <ESC> was pressed, and change the returned value.
I could not hide the "X" button. I tried several aproaches that usually work in normal forms, but all I could do is to disable it.
If the dialog has 3 or more buttons, pressing <ESC> will close the dialog, returning the value 0 - ZERO.



* UPDATE * 2020-05-17
Some new enhancements and small tweaks, introducing the timeout property, and the possibility to use Unicodes, to get some cool characters to enhance our dialogs.


* UPDATE * 2020-05-23
New features:
- Add a custom icon with 6 predefined background colors: Red, Green, Yellow, Blue, Silver, and white
- Add true icons to your buttons

That means that we don't need to use the shield icons to get the colored backgrounds!
Some custom icons were also predefined.

For the buttons, you may use the icons from both "Imageres.dll" and "Shell32.dll"
Pass the captions with an "underline" - "_" symbol and the index number of the icon. By default, the icons will use ImageRes.dll. To use Shlell32.ddl's, add 100000 to the index number, see the samples below:


Comming next:

There is a much empowered version of the TaskDialog API, the TaskDialogIndirect function, that allows us to have several other components in our dialogs, such as Option buttons, Checkboxes, expandable footer, and even a ProgressBar!
I have this already working, with the help of another great VFPX project, VFP2C, that brings us the possibility to make Callback functions in VFP. I hope to publish it very soon.



See Also:


5 comments:

  1. Awesome! Thanks for this huge contribution!

    regards.

    ReplyDelete
  2. Wonderful
    thanks for your share

    ReplyDelete
  3. Brilliant! Thanks a lot master. Great work!

    ReplyDelete
  4. Thank you for this messagebox, it is just what I have been looking for for ages, and you have presented it so well that even a dummy like me can make it work. I have found that when I use it in a top level form in the IDE the form disappears while the dialogue is visible. This doesn't happen in the executable or non-top level forms in the IDE. Am I doing something wrong? Thanks again Alistair

    ReplyDelete
  5. We have a need/want to use custom labels on the buttons using a messagebox form/window. When testing NewDialog() using the 5 Samples, everything worked great and were ready to put it to use. However, when implementing, the return button values (nID) are not correct or consistent. We are getting -1, -2, -3, etc when clicking on the buttons. We are using Ver 2.2 - 2020-05-24.

    (code snippet)

    lcTitle = "Detach OR Delete File(s)"
    lcInstruction = "DETACH or DELETE selected Files" + CHR(13) + "Please select an Action!"
    lcContent = "DETACH: Will remove the selected Files, " + ;
    "but will keep the File(s) in the Document Center." + CHR(13) + CHR(13) + ;
    "DELETE: Will Erase the File(s) from the Document Center"
    lnIcon = "?,B"
    lcButtons = "Delete, Detach, Cancel"
    lnDefaultBtn = 2
    lnTimeout = "80000, secs."

    jnDelete = NewDialog(lcTitle, lcInstruction, lcContent, lnIcon, lcButtons, lnDefaultBtn, lnTimeout)

    DO CASE
    CASE jnDelete = 1
    do something

    Case jnDelete = 2
    do something

    Case jnDelete = -1
    timed out message

    ENDCASE

    ReplyDelete