Inofficial TTaskDialog
Documentation
Andreas Rejbrand, 2011-02-13
Abstract
This document is an inofficial documentation for the TTaskDialog
class introduced in Delphi 2009, but, unfortunately, not documented by the Embarcadero team.
As the name of the class suggests, it is a wrapper for the task dialog API introduced in the Microsoft Windows Vista operating system. The lack of documentation caused quite some confusion in the Delphi community. Although any moderately competent software developer can figure out how to use the class by investigating its members and the VCL source code (using the MSDN documentation if necessary), it is convenient to have a reference to consult, so that one doesn't need to rediscover the workings of the class each time it is used.
The aim of this document is to be such a reference.
The Hello World of A Task Dialog
with TTaskDialog.Create(Self) do try Caption := 'My Application'; Title := 'Hello World!'; Text := 'I am a TTaskDialog, that is, a wrapper for the Task Dialog introduced ' + 'in the Microsoft Windows Vista operating system. Am I not adorable?'; CommonButtons := [tcbClose]; Execute; finally Free; end;
Caption
is the text shown in the titlebar of the window, Title
is the header, and Text
is the body matter of the dialog. Needless to say, Execute
displays the task dialog, and the result is shown below. (We will return to the CommonButtons
property in a section or two.)
Being A Well-Behaved Citizen
Of course, the task dialog will crash the program if running under Windows XP, where there is not task dialog API. It will also not work if visual themes are disabled. In any such case, we need to stick to the old-fashioned MessageBox
. Hence, in a real application, we would need to do
if (Win32MajorVersion >= 6) and ThemeServices.ThemesEnabled then with TTaskDialog.Create(Self) do try Caption := 'My Application'; Title := 'Hello World!'; Text := 'I am a TTaskDialog, that is, a wrapper for the Task Dialog introduced ' + 'in the Microsoft Windows Vista operating system. Am I not adorable?'; CommonButtons := [tcbClose]; Execute; finally Free; end else MessageBox(Handle, 'I am an ordinary MessageBox conveying the same message in order to support' + 'older versions of the Microsoft Windows operating system (XP and below).', 'My Application', MB_ICONINFORMATION or MB_OK);
In the rest of this article, we will assume that the tax of backwards compatibility is being payed, and instead concentrate on the task dialog alone.
Types of Dialogs. Modal Results
The CommonButtons
property is of type TTaskDialogCommonButtons
, defined as
TTaskDialogCommonButton = (tcbOk, tcbYes, tcbNo, tcbCancel, tcbRetry, tcbClose); TTaskDialogCommonButtons = set of TTaskDialogCommonButton;
This property determines the buttons shown in the dialog (if no buttons are added manually, as we will do later on). If the user clicks any of these buttons, the corresponding TModalResult
value will be stored in the ModalResult
property as soon as Execute
has returned. The MainIcon
property determines the icon shown in the dialog, and should -- of course -- reflect the nature of the dialog, as should the set of buttons. Formally an integer, MainIcon
can be set to any of the values tdiNone
, tdiWarning
, tdiError
, tdiInformation
, and tdiShield
.
with TTaskDialog.Create(Self) do try Caption := 'My Application'; Title := 'The Process'; Text := 'Do you want to continue even though [...]?'; CommonButtons := [tcbYes, tcbNo]; MainIcon := tdiNone; // There is no tdiQuestion if Execute then if ModalResult = mrYes then beep; finally Free; end;
Below are samples of the remaining icon types (shield, warning, and error, respectively):
Finally, you should know that you can use the DefaultButton
property to set the default button in the dialog box.
with TTaskDialog.Create(Self) do try Caption := 'My Application'; Title := 'The Process'; Text := 'Do you want to continue even though [...]?'; CommonButtons := [tcbYes, tcbNo]; DefaultButton := tcbNo; MainIcon := tdiNone; if Execute then if ModalResult = mrYes then beep; finally Free; end;
Custom Buttons
You can add custom buttons to a task dialog. In fact, you can set the CommonButtons
property to the empty set, and rely entirely on custom buttons (and un unlimited number of such buttons, too). The following real-world example shows such a dialog box:
with TTaskDialog.Create(self) do try Title := 'Confirm Removal'; Caption := 'Rejbrand BookBase'; Text := Format('Are you sure that you want to remove the book file named "%s"?', [FNameOfBook]); CommonButtons := []; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Remove'; ModalResult := mrYes; end; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Keep'; ModalResult := mrNo; end; MainIcon := tdiNone; if Execute then if ModalResult = mrYes then DoDelete; finally Free; end
Command Links
Instead of classical pushbuttons, the task dialog buttons can be command links. This is achieved by setting the tfUseCommandLinks
flag (in Flags
). Now you can also set the CommandLinkHint
(per-button) property:
with TTaskDialog.Create(self) do try Title := 'Confirm Removal'; Caption := 'Rejbrand BookBase'; Text := Format('Are you sure that you want to remove the book file named "%s"?', [FNameOfBook]); CommonButtons := []; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Remove'; CommandLinkHint := 'Remove the book from the catalogue.'; ModalResult := mrYes; end; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Keep'; CommandLinkHint := 'Keep the book in the catalogue.'; ModalResult := mrNo; end; Flags := [tfUseCommandLinks]; MainIcon := tdiNone; if Execute then if ModalResult = mrYes then DoDelete; finally Free; end
The tfAllowDialogCancellation
flag will restore the close system menu item (and titlebar button -- in fact, it will restore the entire system menu).
Don't Throw Technical Details at the End User
You can use the properties ExpandedText
and ExpandedButtonCaption
to add a piece of text (the former) that is only displayed after the user clicks a button (to the left of the text in the latter property) to request it.
with TTaskDialog.Create(self) do try Title := 'Confirm Removal'; Caption := 'Rejbrand BookBase'; Text := Format('Are you sure that you want to remove the book file named "%s"?', [FNameOfBook]); CommonButtons := []; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Remove'; CommandLinkHint := 'Remove the book from the catalogue.'; ModalResult := mrYes; end; with TTaskDialogButtonItem(Buttons.Add) do begin Caption := 'Keep'; CommandLinkHint := 'Keep the book in the catalogue.'; ModalResult := mrNo; end; Flags := [tfUseCommandLinks, tfAllowDialogCancellation]; ExpandButtonCaption := 'Technical information'; ExpandedText := 'If you remove the book item from the catalogue, the corresponding *.book file will be removed from the file system.'; MainIcon := tdiNone; if Execute then if ModalResult = mrYes then DoDelete; finally Free; end
The image below shows the dialog after the user has clicked the button to reveal the additional details.
If you add the tfExpandFooterArea
flag, the additional text will instead be shown in the footer:
In any case, you can let the dialog open with the details already expanded by adding the tfExpandedByDefault
flag.
Custom Icons
You can use any custom icon in a task dialog, by using the tfUseHiconMain
flag and specifying the TIcon
to use in the CustomMainIcon
property.
with TTaskDialog.Create(self) do try Caption := 'About Rejbrand BookBase'; Title := 'Rejbrand BookBase'; CommonButtons := [tcbClose]; Text := 'File Version: ' + GetFileVer(Application.ExeName) + #13#10#13#10'Copyright © 2011 Andreas Rejbrand'#13#10#13#10'http://english.rejbrand.se'; Flags := [tfUseHiconMain, tfAllowDialogCancellation]; CustomMainIcon := Application.Icon; Execute; finally Free; end
Hyperlinks
You can even use HTML-like hyperlinks in the dialog (in Text
, Footer
, and ExpandedText
), if you only add the tfEnableHyperlinks
flag:
with TTaskDialog.Create(self) do try Caption := 'About Rejbrand BookBase'; Title := 'Rejbrand BookBase'; CommonButtons := [tcbClose]; Text := 'File Version: ' + GetFileVer(Application.ExeName) + #13#10#13#10'Copyright © 2011 Andreas Rejbrand'#13#10#13#10'<a href="http://english.rejbrand.se">http://english.rejbrand.se</a>'; Flags := [tfUseHiconMain, tfAllowDialogCancellation, tfEnableHyperlinks]; CustomMainIcon := Application.Icon; Execute; finally Free; end
Notice, however, that nothing happens when you click the link. The action of the link must be implemented manually, which -- of course -- is a good thing. To do this, respond to the OnHyperlinkClicked
event, which is a TNotifyEvent
. The URL of the link (the href
of the a
element, that is) is stored in the URL
public property of the TTaskDialog
:
procedure TForm1.TaskDialogHyperLinkClicked(Sender: TObject); begin if Sender is TTaskDialog then with Sender as TTaskDialog do ShellExecute(0, 'open', PChar(URL), nil, nil, SW_SHOWNORMAL); end; procedure TForm1.FormCreate(Sender: TObject); begin with TTaskDialog.Create(self) do try Caption := 'About Rejbrand BookBase'; Title := 'Rejbrand BookBase'; CommonButtons := [tcbClose]; Text := 'File Version: ' + GetFileVer(Application.ExeName) + #13#10#13#10'Copyright © 2011 Andreas Rejbrand'#13#10#13#10'<a href="http://english.rejbrand.se">http://english.rejbrand.se</a>'; Flags := [tfUseHiconMain, tfAllowDialogCancellation, tfEnableHyperlinks]; OnHyperlinkClicked := TaskDialogHyperlinkClicked; CustomMainIcon := Application.Icon; Execute; finally Free; end end;
The Footer
You can use the Footer
and FooterIcon
properties to create a footer. The icon property accepts the same values as the MainIcon
property.
with TTaskDialog.Create(self) do try Caption := 'My Application'; Title := 'A Question'; Text := 'This is a really tough one...'; CommonButtons := [tcbYes, tcbNo]; MainIcon := tdiNone; FooterText := 'If you do this, then ...'; FooterIcon := tdiWarning; Execute; finally Free; end
Using the tfUseHiconFooter
flag and the CustomFooterIcon
property, you can use any custom icon in the footer, in the same way as you can choose your own main icon.
A Checkbox
Using the VerificationText
string property, you can add a checkbox to the footer of the task dialog. The caption of the checkbox is the property.
with TTaskDialog.Create(self) do try Caption := 'My Application'; Title := 'A Question'; Text := 'This is a really tough one...'; CommonButtons := [tcbYes, tcbNo]; MainIcon := tdiNone; VerificationText := 'Remember my choice'; Execute; finally Free; end
You can make the checkbox initially checked by specifying the tfVerificationFlagChecked
flag. Unfortunately, due to a bug (?) in the VCL implementation of the TTaskDialog
, the inclusion of this flag when Execute
has returned doesn't reflect the final state of the checkbox. To keep track of the checkbox, the application thus needs to remember the initial state and toggle an internal flag as a response to each OnVerificationClicked
event, which is triggered every time the state of the checkbox is changed during the modality of the dialog.
Radio Buttons
Radio buttons can be implemented in a way resembling how you add custom push buttons (or command link buttons):
with TTaskDialog.Create(self) do try Caption := 'My Application'; Title := 'A Question'; Text := 'This is a really tough one...'; CommonButtons := [tcbOk, tcbCancel]; MainIcon := tdiNone; with RadioButtons.Add do Caption := 'This is one option'; with RadioButtons.Add do Caption := 'This is another option'; with RadioButtons.Add do Caption := 'This is a third option'; if Execute then if ModalResult = mrOk then ShowMessage(Format('You chose %d.', [RadioButton.Index])); finally Free; end