-
4 Attachment(s)
[VB6/VBA/tB] TaskDialogIndirect - Complete class implementation of Task Dialogs
cTaskDialog 1.7 Universal
Say goodbye to unsightly, primitive messageboxes forever with the new enhanced TaskDialog.
Updated released 20 Feb 2026
Attachment 178039
cTaskDialog is the sequel to my previous TaskDialogIndirect project, mTaskDialog. This version adds support for all TaskDialogIndirect features, including the progress bar, timer feedback, updating the text and icons while the dialog is open, and events for all the notifications. It's also much easier to use. VB6 can use many modern Wndows features, and the TaskDialog is one of the most useful.
**Recently Added Features**
Project Update - 20 Feb 2026
Add InputBoxMaxLength property to set maximum number of characters.
Apply temporary fix for icon/button matching: #18
Temp fix only applies when both radio buttons and command links are used, until I have a chance to investigate other scenarios.
Project Update - 02 Dec 2025
Version 1.6 R2 corrects the omission of custom icon support for the Help common button.
Version 1.6 Universal adds .InputPasswordChar to set any character as the password char and correctly updates an active dialog if needed, custom icon support for the Abort/Ignore/Try Again/Continue common buttons, and a number of long overdue bug fixes.
Project Update - 15 Jun 2024
Version 1.5 Universal adds request features for Combo Item lParam access, and no longer requires a manifest. While it's still recommended you use one, if it detects comctl6 isn't present, it will now use the SxS Activation Context API to briefly activate it when calling TaskDialogIndirect so you no longer get an entry point not found error. This is primarily for 32bit Excel, which for some reason doesn't have comctl6 enabled like other 32bit Office apps and 64bit Excel; again, other sources should use a manifest if one isn't present.
Project Update - 17 Jan 2024
Additional undocumented Common Buttons added (abort, retry, ignore, and continue).
Project Update - 08 Oct 2023
Updating this post to Version 1.3 Universal. The features are the same with some bug fixes, but this version universally supports: VB6, VBA6, VBA7 32bit, VBA7 64bit, twinBASIC 32bit, and twinBASIC 64bit..
IMPORTANT: For compatibility, this version no longer uses self-subclassing, and like earlier versions, once again requires mTDHelper.bas in all projects. (mTDSample.bas is still only for the demo form).
|
What is TaskDialog?
TaskDialog, introduced in Windows Vista, is a massive upgrade to the MessageBox. While not as simple, it offers a huge array of features... custom icons, custom button text, command link style buttons, expanded info button, a footer, a checkbox for 'i read this' or 'don't show this again' type messages, radio buttons, hyperlinks, and more.
This project can be used to create a simple messagebox like the older ones, to an extremely complex dialog with all the features mentioned, even all at once!
Before using cTaskDialog
The TaskDialog was introduced with version 6.0 of the common controls, with Windows Vista. That means a manifest is required for your compiled application, and for VB6.exe in order to use it from the IDE. The sample project includes Sub Main(), and see LaVolpe's Manifest Creator to create the manifests.
Setting Up cTaskDialog
Your project must include cTaskDialog.cls and mTDHelper.bas
mTDSample.bas is used only for the sample project.
Once you've got a project using the modern common controls, cTaskDialog is similar in use to a lot of other class modules, like the other common controls.
To initialize the class, put the following at the start of a form and in the Form_Load code:
Code:
Private WithEvents TaskDialog1 As cTaskDialog
Private Sub Form_Load()
Set TaskDialog1 = New cTaskDialog
End Sub
In Brief: The simple TaskDialog()
TaskDialogIndirect is the real rockstar, but the class supports the regular TaskDialog as well.
Function SimpleDialog(sMessage As String, Optional dwBtn As TDBUTTONS = TDCBF_OK_BUTTON, Optional sTitle As String, Optional sMainText As String, Optional dwIco As TDICONS, Optional hwndOwner As Long, Optional hInst As Long) As TDBUTTONS
The first 3 arguments are the same order as MsgBox, so it's a very quick replacement with the only requirement being to change the buttons argument. Usage is very simple:
Code:
Dim td As TDBUTTONS
td = TaskDialog1.SimpleDialog("Is TaskDialogIndirect going to be better than this?", TDCBF_YES_BUTTON, App.Title, "This is regular old TaskDialog", TD_SHIELD_GRAY_ICON, Me.hWnd, App.hInstance)
Label1.Caption = "ID of button clicked: " & bt
As with other implementations, you can specify the index of an icon in the resource file for your app, or another app/dll by changing the hInstance.
But that's not really why we're here. Let's begin using the main ShowDialog() function that uses TaskDialogIndirect.
We'll start simple, with a box like we've seen before. Creating this box is very straightforward. Unlike the previous incarnation, you don't have to worry about anything you're not using.
Code:
With TaskDialog1
.MainInstruction = "This is a simple dialog."
.CommonButtons = TDCBF_YES_BUTTON Or TDCBF_NO_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ShowDialog
If .ResultMain = TD_YES Then
Label1.Caption = "Yes Yes Yes!"
ElseIf .ResultMain = TD_NO Then
Label1.Caption = "Nope. No. Non. Nein."
Else
Label1.Caption = "Cancelled."
End If
End With
That's all it takes for a basic messagebox. The .Init() call resets the dialog. If you want to re-use all the previous settings and just change a couple things, it can be skipped.
If you put your text in .Content and nothing in .MainInstruction, it will look like the dialog on the bottom. |
Attachment 178040
Attachment 178041 |
Now that basic usage is covered, let's get down to what you really came for: advanced features!
Here's a few basic changes that make a much more fancy looking dialog:
Code:
.Init
.MainInstruction = "You're about to do something stupid."
.Content = "Are you absolutely sure you want to continue with this really bad idea?"
.CommonButtons = TDCBF_YES_BUTTON Or TDCBF_NO_BUTTON
.IconMain = TD_SHIELD_WARNING_ICON 'TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ShowDialog
The TaskDialog supports several special shield icons that create the colored bar uptop. If you use a regular icon, or a custom icon, it will look like the dialog on the bottom.
All the other text fields are added the same way, so I'm just going to skip over those. One thing to note, with expanded information set, the little expando button appears automatically when you set those fields and requires no additional code to operate; where it appears is set by a flag, which is described later. Also note that the major text fields can be changed while the dialog is open, just set it again the same way. |
https://www.vbforums.com/images/ieimages/2014/09/6.gif
https://www.vbforums.com/images/ieimages/2014/09/7.gif |
One of the big features is the ability to customize the text on the buttons. Due to limitations in VB, I've implemented them by using a .AddButton function. You can assign the button the same id as one of the regular buttons, or give it a unique id. Custom buttons can be removed with .ClearCustomButtons so a full Init() call isn't needed.
Code:
With TaskDialog1
.Init
.MainInstruction = "You're about to do something stupid."
.Content = "Are you absolutely sure you want to continue with this really bad idea?"
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.AddCustomButton 101, "YeeHaw!"
.AddCustomButton 102, "NEVER!!!"
.AddCustomButton 103, "I dunno?"
.ShowDialog
Label1.Caption = "ID of button clicked: " & .ResultMain
End With
Note that we have removed the .CommonButtons. If you specify buttons there as well, they will appear in addition to your custom buttons. We'll get to that hIcon option when we cover custom icons. |
https://www.vbforums.com/images/ieimages/2014/09/3.jpg |
Radio buttons are added the exact same way as custom buttons; and the ID of the radio button selected is found in the .ResultRad property.
Code:
.AddRadioButton 110, "Let's do item 1"
.AddRadioButton 111, "Or maybe 2"
.AddRadioButton 112, "super secret option"
.ShowDialog
Label1.Caption = "ID of button clicked: " & .ResultMain
Label2.Caption = "ID of radio button selected: " & .ResultRad
|
https://www.vbforums.com/images/ieimages/2014/09/4.jpg |
One of the other biggies are Hyperlinks. These require a few additional steps. First, you need to include TDF_ENABLE_HYPERLINKS in the .Flags property. Then, you add in the hyperlink as normal html, but the url needs to be in quotes. Then you'll need to use one of the events for the class. The most common thing to do is just execute the link, so that's what's shown here, but you could also get the URL back from the pointer and do something else with it. You must use ShellExecuteW, not ShellExecuteA (which is normally what just plain ShellExecute points to). The declare is included in the sample project.
Code:
With TaskDialog1
.Init
.MainInstruction = "Let's see some hyperlinking!"
.Content = "Where else to link to but <a href=""http://www.microsoft.com"">Microsoft.com</a>"
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.CommonButtons = TDCBF_CLOSE_BUTTON
.Flags = TDF_ENABLE_HYPERLINKS
.ShowDialog
End With
Private Sub TaskDialog1_HyperlinkClick(ByVal lPtr As Long)
Call ShellExecuteW(0, 0, lPtr, 0, 0, SW_SHOWNORMAL)
End Sub
|
https://www.vbforums.com/images/ieimages/2014/09/5.jpg |
Let's talk about custom icons. You can have a custom icon for both the main icon and the footer icon.
Thanks to the brilliant idea of Schmidt over here, the option to specify an icon id from either shell32.dll or imageres.dll, the two main Windows icon libraries, has been added. This expands the icons you can show without using an hIcon.
TDF_USE_SHELL32_ICONID, TDF_USE_IMAGERES_ICONID
Simply specify if you're going to use one of those sources by adding the above to the flags, and set the icon to the index you want. Only one can be used, so both header and footer index will come from the chosen DLL. Standard icons (the TDICONS group) work regardless of the source of other icons. NOTE: These do not run from 0-# of icons, so if your icon browser is telling you that, you need to use a different one, otherwise the dialog may display the wrong icon, or not show at all if the id doesn't exist.
Code:
With TaskDialog1
.Init
.MainInstruction = "Show me the icons!"
.Content = "Yeah, that's the stuff."
.Footer = "Got some footer icon action here too."
.Flags = TDF_USE_SHELL32_ICONID
.IconMain = 18
.IconFooter = 35
.Title = "cTaskDialog Project"
.CommonButtons = TDCBF_CLOSE_BUTTON
.ShowDialog
End With
|
https://www.vbforums.com/images/ieimages/2014/09/6.jpg |
You can also specify a truly custom icon from a .ico file on disk, in your resource file, or anywhere you can get an hIcon from.
The sample project uses a method I adapted from Leandro Ascierto's cMenuImage. It gets around VB's limitations on icons by adding them to the resource file as a custom resource- allowing any size, color depth, and # of entries. Then, the ResIcontoHICON function gives the hIcon. Also any other function returning an hIcon will work. Icons can be updated while the dialog is open by another .IconMain= or .IconFooter= statement. You can use a standard icon for main and custom for footer, and vice versa, or both, by adding TDF_USE_HICON_MAIN/TDF_USE_HICON_FOOTER to the flags. If you create an hIcon, don't forget to destroy it when you're done.
The icon size can't be changed much; the main icon will be distorted but not larger if you give it a larger size, although you can make it a smaller size. The footer icon won't change at all.
Code:
Dim hIconM As Long, hIconF As Long
hIconM = ResIconTohIcon("ICO_CLOCK", 32, 32)
hIconF = ResIconTohIcon("ICO_HEART", 16, 16)
With TaskDialog1
.Init
.MainInstruction = "What time is it?"
.Content = "Is is party time yet???"
.Footer = "Don't you love TaskDialogIndirect?"
.Flags = TDF_USE_HICON_MAIN Or TDF_USE_HICON_FOOTER
.IconMain = hIconM
.IconFooter = hIconF
.Title = "cTaskDialog Project"
.CommonButtons = TDCBF_CLOSE_BUTTON
.ShowDialog
End With
Call DestroyIcon(hIconM)
Call DestroyIcon(hIconF)
Due to the limitations on what icons can be put in a VB project res file icon group, a different method is needed. If you do want to use those anyway, add .hInst = App.hInstance
Shell32/imageres and fully custom icons are supported for buttons as well. When you use .AddCustomButton and .SetCommonButtonIcon, you can specify an hIcon, or an index and add TDF_USE_<shell32/imageres>_ICONID_BUTTON.
Code:
hIcon1 = ResIconToHICON("ICO_CLOCK", 16, 16)
.Flags = TDF_USE_SHELL32_ICONID_BUTTON Or TDF_USE_COMMAND_LINKS
.CommonButtons = TDCBF_CLOSE_BUTTON Or TDCBF_NO_BUTTON
.AddCustomButton 103, "Button 1"
.AddCustomButton 102, "Button 2", hIcon2
.SetWindowsButtonIconSize ICO_32
.SetCommonButtonIcon TDCBF_NO_BUTTON, hIcon1
|
https://www.vbforums.com/images/ieimages/2014/09/1.gif
https://www.vbforums.com/images/ieimages/2014/09/2.gif |
The last basic feature is the verification checkbox. Here's an example with that, and all the other text fields.
Code:
With TaskDialog1
.Init
.MainInstruction = "Let's see all the basic fields."
.Content = "We can really fit in a lot of organized information now."
.Title = "cTaskDialog Project"
.Footer = "Have some footer text."
.CollapsedControlText = "Click here for some more info."
.ExpandedControlText = "Click again to hide that extra info."
.ExpandedInfo = "Here's a whole bunch more information you probably don't need."
.VerifyText = "Never ever show me this dialog again!"
.IconMain = TD_INFORMATION_ICON
.IconFooter = TD_ERROR_ICON
.ShowDialog
Label1.Caption = "ID of button clicked: " & .ResultMain
End With
|
https://www.vbforums.com/images/ieimages/2014/09/3.gif |
One of the major stylistic differences are the CommandLink buttons. When using the Command Link style, the first line is considered the main text, and lines are made into sub-text. Note that the line is broken with vbLf only; not vbCrLf. vbCrLf results in the text not being smaller on Win7 x64, I haven't tested other systems but it should be the same.
With the custom button sample from above, these changes are made:
Code:
.Flags = TDF_USE_COMMAND_LINKS
.AddCustomButton 101, "YeeHaw!" & vbLf & "Put some additional information about the command here."
|
https://www.vbforums.com/images/ieimages/2014/09/10.jpg |
Advanced Features
The TaskDialog supports having a progress bar, both regular and marquee. To enable it, include the TDF_SHOW_PROGRESS_BAR or the TDF_SHOW_MARQUEE_PROGRESS_BAR flag (you can switch back and forth between them while the dialog is open if you want). Getting it to show up is the easy part, linking it to actual events in your program is where it gets a little tricky. There's some events that are provided that will help out...
TaskDialog_DialogCreated is triggered when the dialog is displayed, then all the buttons, the radio buttons, the expando button, the checkbox, and hyperlinks all have events when the user clicks them. In addition to that, TaskDialog_Timer is sent approximately every 200ms and includes a variable telling you how many ms has elapsed since the dialog appeared, or since it was reset with the .ResetTimer() call. The example shows a basic counter, but you can go further and enable/disable buttons and use hyperlinks to control things too.
Code:
Private bRunProgress As Boolean
Private lSecs As Long
With TaskDialog1
.Init
.MainInstruction = "You're about to do something stupid."
.Content = "Are you absolutely sure you want to continue with this really bad idea? I'll give you a minute to think about it."
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.Footer = "Really, think about it."
.Flags = TDF_USE_COMMAND_LINKS Or TDF_SHOW_PROGRESS_BAR Or TDF_CALLBACK_TIMER
.AddCustomButton 101, "YeeHaw!" & vbLf & "Put some additional information about the command here."
.AddCustomButton 102, "NEVER!!!"
.AddCustomButton 103, "I dunno?"
.VerifyText = "Hold up!"
bRunProgress = True
.ShowDialog
End With
Private Sub TaskDialog1_DialogCreated(ByVal hWnd As Long)
If bRunProgress Then
Timer1.Interval = 1000
Timer1.Enabled = True
TaskDialog1.ProgressSetRange 0, 60
End If
End Sub
Private Sub TaskDialog1_Timer(ByVal TimerValue As Long)
If lSecs > 60 Then
Timer1.Enabled = False
bRunProgress = False
Else
TaskDialog1.ProgressSetValue lSecs
TaskDialog1.Footer = "You've been thinking for " & lSecs & " seconds now..."
End If
End Sub
Private Sub TaskDialog1_VerificationClicked(ByVal Value As Long)
If Value = 1 Then
Timer1.Enabled = False
bRunProgress = False
Else
bRunProgress = True
Timer1.Enabled = True
End If
End Sub
Private Sub Timer1_Timer()
lSecs = lSecs + 1
End Sub
|
https://www.vbforums.com/images/ieimages/2014/09/4.gif |
Microsoft didn't document it very well, but it did provide a mechanism to have multiple pages. With multiple pages, you can have a command that takes the user forward to the next dialog in the sequence, or back to the previous one. Setting this up is a little complicated, but not too bad.
1) Create a new cTaskDialog and initialize the same as any other dialog.
2) Fill out all the configuration info you need before calling the first dialog.
3) When you add a button to a dialog to go forward or back, you must also use .SetButtonHold, otherwise the dialog closes when the button is clicked.
4) Add code to call .NavigatePage in the .ButtonClick event.
5) The new page will send a .Navigated event; this is instead of .DialogCreated. Also note that only the dialog that exits and returns a final result will send the .DialogDestroyed event.
Code:
Private WithEvents TaskDialog2 As cTaskDialog
Set TaskDialog2 = New cTaskDialog
With TaskDialog2
.Init
.Content = "Here's a whole new dialog with all the options."
.CommonButtons = TDCBF_OK_BUTTON
.IconMain = TD_SHIELD_OK_ICON
.Title = "cTaskDialog Project - Page 2"
End With
With TaskDialog1
.Init
.MainInstruction = "You can now have multiple pages."
.Content = "Click Next Page to continue."
.Flags = TDF_USE_COMMAND_LINKS
.AddCustomButton 200, "Next Page" & vbLf & "Click here to continue to the next TaskDialog"
.CommonButtons = TDCBF_YES_BUTTON Or TDCBF_NO_BUTTON
.IconMain = TD_SHIELD_WARNING_ICON
.SetButtonHold 200
.Title = "cTaskDialog Project - Page 1"
.ShowDialog
End With
Private Sub TaskDialog1_ButtonClick(ByVal ButtonID As Long)
If ButtonID = 200 Then
TaskDialog1.NavigatePage TaskDialog2
End If
End Sub
|
https://www.vbforums.com/images/ieimages/2014/09/5.gif |
Here are the progress bar calls:
| .ProgressSetRange(Min,Max) |
Sets the range |
| .ProgressSetState(state) |
Sets the progress bar state; ePBST_NORMAL for a green bar, ePBST_PAUSED for yellow, ePBST_ERROR for red. |
| .ProgressSetValue(value) |
Sets the value of the progress bar. |
| .ProgressSetType(value) |
Allows switching between a regular bar and marquee style bar. 0 for regular, 1 for marquee. |
| .ProgressStartMarquee([speed]) |
Starts the marquee; the bar must be set to marquee style, either originally, or changed to it, in order for this to work. If speed is not specified, the default of 30 is used. |
| .ProgressStopMarquee() |
Stops the marquee. |
Here's the code for the everything box at the top; note how the use of the cancel button adds the X and icon to the title bar (those can appear without a cancel button using the TDF_ALLOW_DIALOG_CANCELLATION flag).
Code:
Private bRunMarquee As Boolean
Dim hIconM As Long, hIconF As Long
hIconM = ResIconTohIcon("ICO_CLOCK", 32, 32)
hIconF = ResIconTohIcon("ICO_HEART", 16, 16)
With TaskDialog1
.Init
.MainInstruction = "Let's see it all!"
.Content = "Lots and lots of features are possible, thanks <a href=" & Chr(34) & "http://www.microsoft.com" & Chr(34) & ">Microsoft</a>"
.IconMain = hIconM
.IconFooter = hIconF
.Flags = TDF_USE_HICON_MAIN Or TDF_USE_HICON_FOOTER Or TDF_ENABLE_HYPERLINKS Or TDF_USE_COMMAND_LINKS Or TDF_SHOW_MARQUEE_PROGRESS_BAR Or TDF_CAN_BE_MINIMIZED
.Title = "cTaskDialog Project"
.Footer = "Have some footer text."
.CollapsedControlText = "Click here for some more info."
.ExpandedControlText = "Click again to hide that extra info."
.ExpandedInfo = "Here's a whole bunch more information you probably don't need."
.VerifyText = "Never ever show me this dialog again!"
.CommonButtons = TDCBF_RETRY_BUTTON Or TDCBF_CANCEL_BUTTON Or TDCBF_CLOSE_BUTTON Or TDCBF_YES_BUTTON
.AddCustomButton 101, "YeeHaw!" & vbLf & "Some more information describing YeeHaw"
.AddCustomButton 102, "NEVER!!!"
.AddCustomButton 103, "I dunno?" & vbLf & "Or do i?"
.AddRadioButton 110, "Let's do item 1"
.AddRadioButton 111, "Or maybe 2"
.AddRadioButton 112, "super secret option"
.EnableRadioButton 112, 0
.EnableButton 102, 0
.SetButtonElevated TD_RETRY, 1
bRunMarquee = True
.ShowDialog
bRunMarquee = False
End With
Private Sub TaskDialog1_DialogCreated(ByVal hWnd As Long)
If bRunProgress Then
Timer1.Enabled = True
TaskDialog1.ProgressSetRange 0, 60
End If
If bRunMarquee Then
TaskDialog1.ProgressStartMarquee
End If
End Sub
That's the basic feature set. The class allows an infinite number of customizations to take place from here.
(continued in next post)
[
IMPORTANT: For compatibility, this version no longer uses self-subclassing, and like earlier versions, once again requires mTDHelper.bas in all projects. (mTDSample.bas is still only for the demo form).
-
5 Attachment(s)
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
(continued, too long)
CUSTOMIZATIONS
Below covers the additional custom controls.
See the full change logs and other upgrades for Version 0.8, and for Version 1.0.
Starting in v0.7, I began adding additional controls. First was the input box.
First, a basic input box with the default positioning:
Code:
With TaskDialog1
.Init
.MainInstruction = "Hello World"
.Content = "Input Required"
.Flags = TDF_INPUT_BOX
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Label5.Caption = .ResultInput
If .ResultMain = TD_OK Then
Label1.Caption = "Yes Yes Yes!"
Else
Label1.Caption = "Cancelled."
End If
End With
|
Attachment 194441 |
We can also position it next to the buttons:
Code:
With TaskDialog1
.Init
.MainInstruction = "Input Required"
.Content = "Tell me what I want to know!" & vbCrLf '& vbCrLf
.Flags = TDF_INPUT_BOX
.InputAlign = TDIBA_Buttons
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
|
Attachment 194442 |
The last placement option is to use it as footer input. It's designed to use the normal footer icon, but have a textbox instead of a label:
Code:
With TaskDialog1
.Init
.Content = "Something somesuch hows-it what-eva" '& vbCrLf
.Flags = TDF_INPUT_BOX 'Or TDF_USE_SHELL32_ICONID
.InputAlign = TDIBA_Footer
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconFooter = TD_INFORMATION_ICON
.VerifyText = "Check here if you want to provide extra info below:"
.Title = "cTaskDialog Project"
.Footer = "$input"
.ParenthWnd = Me.hWnd
.ShowDialog
|
Attachment 194443 |
Here's a practical example showing it with default text (which is pre-selected automatically):
Code:
With TaskDialog1
.Init
.MainInstruction = "Duplicates"
.Content = "If you want to exclude an Artists name from the search:" & vbCrLf & vbCrLf
.Flags = TDF_INPUT_BOX Or TDF_VERIFICATION_FLAG_CHECKED 'Or TDF_USE_SHELL32_ICONID
.AddCustomButton 100, "Continue"
.CommonButtons = TDCBF_CANCEL_BUTTON
.IconMain = TD_SHIELD_ICON
.Title = "cTaskDialog Project"
.InputText = "Enter Artist name here."
.VerifyText = "Exclude Jingles"
.ParenthWnd = Me.hWnd
.ShowDialog
Note the spacing; there's always a single line break like the first one (appended if not present), but using a double-break relaxes the spacing a bit.
|
Attachment 194444 |
If CommandLinks are being used, the default alignment (content) places the textbox between the content and the first commandlink:
Code:
With TaskDialog1
.Init
.MainInstruction = "You're about to do something stupid."
.Content = "First, tell me why?"
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.Flags = TDF_USE_COMMAND_LINKS Or TDF_INPUT_BOX
.AddCustomButton 101, "YeeHaw!" & vbLf & "Put some additional information about the command here."
.AddCustomButton 102, "NEVER!!!"
.AddCustomButton 103, "I dunno?"
.ShowDialog
There's also compatibility with the expanded-info control in the default positioning. The position of the box is automatically adjusted when the expando control is clicked.
|
Attachment 194445 |
Finally here's a detailed practical application that shows some more features. The textbox can be set as a password box. We can then combine that with a button hold, so now when enter or ok is pressed, we check the input and only let OK execute if it's a match- while cancel still closes it right away.
Code:
Set TaskDialogPW = New cTaskDialog
With TaskDialogPW
.Init
.MainInstruction = "Authorization Required"
.Content = "The password is: password"
.Flags = TDF_INPUT_BOX
.InputIsPassword = True
.InputAlign = TDIBA_Buttons
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.SetButtonElevated TD_OK, 1
.SetButtonHold TD_OK
.Footer = "Enter your password then press OK to continue."
.IconFooter = TD_INFORMATION_ICON
.IconMain = TD_SHIELD_ERROR_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Then we need to handle the ok button click to check the password:
Code:
Private Sub TaskDialogPW_ButtonClick(ByVal ButtonID As Long)
If ButtonID = TD_OK Then
If TaskDialogPW.InputText = "password" Then
TaskDialogPW.CloseDialog
Else
TaskDialogPW.Footer = "Wrong password, please try again."
TaskDialogPW.IconFooter = TD_ERROR_ICON
End If
End If
End Sub
|
https://www.vbforums.com/attachment....hmentid=194446
https://www.vbforums.com/attachment....hmentid=194447 |
After the success of the Input Box in 0.7, version 0.8 of cTaskDialog adds even more useful controls added by custom flag. There's now 4 types of controls:
TDF_INPUT_BOX The textbox from 0.7
TDF_COMBO_BOX A combo box that can either be an editable one or a dropdown list. It's a ComboBoxEx (ImageCombo), so it accepts an imagelist in the form of an HIMAGELIST.
TDF_DATETIME Shows a datetime control; either a single one for date or time, or two controls for date AND time.
TDF_SLIDER A standard slider control with detailed options for the range and ticks.
Following in the tradition of the existing control options, you can add one of each of these (up to 3) in the same 3 areas where the inputbox could be placed: in the main content area, next to the buttons (replaces the expando and/or verify controls if placed here), or as the footer (the footer icon shows and is properly aligned, but old footer text is covered). Thanks to some painstaking calculations and testing, all these controls are additional options: you can use any number and combination of the built-in controls along with the new custom ones, including the expando control, and a space for them is automatically created. The creation and usage of the new controls follows the exact same easy format of the rest of the class.
Here's some selected samples; the attached project contains many additional ones showing the different options.
Code:
himlSys = GetSystemImagelist(SHGFI_SMALLICON) 'any image list will do; make your own with ImageList_Create or IImageList
With TaskDialog3
.Init
.MainInstruction = "Duplicates"
.Content = "If you want to exclude an Artists name from the search:"
.Flags = TDF_VERIFICATION_FLAG_CHECKED Or TDF_COMBO_BOX
.AddCustomButton 100, "Continue"
.CommonButtons = TDCBF_CANCEL_BUTTON
.IconMain = TD_SHIELD_ICON
.Title = "cTaskDialog Project"
.ComboCueBanner = "Cue Banner Text"
.ComboSetInitialState "", 5
' .ComboSetInitialItem 1
.ComboImageList = himlSys
.ComboAddItem "Item 1", 6
.ComboAddItem "Item 2", 7
.ComboAddItem "Item 3", 8
.VerifyText = "Exclude Jingles"
.ParenthWnd = Me.hWnd
.ShowDialog
Label3.Caption = "Checked? " & .ResultVerify
Label7.Caption = .ResultComboText
Label9.Caption = .ResultComboIndex
If .ResultMain = 100 Then
Label1.Caption = "Continue!"
Else
Label1.Caption = "Cancelled."
End If
End With
|
https://www.vbforums.com/attachment....hmentid=194448 |
Here we add pre-selected users to our password demo. The partial code below shows how dual custom controls are used:
Code:
.Flags = TDF_INPUT_BOX Or TDF_COMBO_BOX
.ComboStyle = cbtDropdownList
.InputIsPassword = True
.InputAlign = TDIBA_Buttons
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.SetButtonElevated TD_OK, 1
.SetButtonHold TD_OK
.ComboAlign = TDIBA_Content
.ComboSetInitialItem 0
.ComboImageList = himlSys
.ComboAddItem "User 1", 6
.ComboAddItem "User 2", 7
.ComboAddItem "User 3", 8
.Footer = "Enter your password then press OK to continue."
.IconFooter = TD_INFORMATION_ICON
.IconMain = TD_SHIELD_ERROR_ICON
|
https://www.vbforums.com/attachment....hmentid=194449 |
The most basic Date control. This can also be a time control.
Code:
.Init
.MainInstruction = "Hello World"
.Content = "Pick a day, any day" & vbCrLf & vbCrLf
.Flags = TDF_DATETIME
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Label11.Caption = .ResultDateTime
|
https://www.vbforums.com/attachment....hmentid=194450 |
Many common options are supported. This example shows combining the new controls with the built-in ones, and setting a limited range in the date and time controls. When the two controls are used, both the chosen date and time are represented by .ResultDateTime.
Code:
Dim dTimeMin As Date, dTimeMax As Date
dTimeMin = DateSerial(Year(Now), Month(Now), Day(Now)) + TimeSerial(13, 0, 0)
dTimeMax = DateAdd("d", 7, dTimeMin)
dTimeMax = DateAdd("h", 4, dTimeMax)
With TaskDialog1
.Init
.MainInstruction = "Date Ranges"
.Content = "Pick a time, limited to sometime in the next 7 days, between 1pm and 6pm" & vbCrLf & vbCrLf
.Flags = TDF_DATETIME Or TDF_USE_COMMAND_LINKS
.DateTimeType = dttDateTime
.DateTimeAlign = TDIBA_Content
.DateTimeSetRange True, True, dTimeMin, dTimeMax
.DateTimeSetInitial dTimeMin
.AddCustomButton 101, "Set Date" & vbLf & "Apply this date and time to whatever it is you're doing."
.CommonButtons = TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Label11.Caption = .ResultDateTime
|
https://www.vbforums.com/attachment....hmentid=194451 |
Here's a highly customized slider combined with showing the auto-positioning that allows the expando control to be used, even with built-in controls further upping the complexity.
Code:
.Init
.MainInstruction = "Sliding on down"
.Content = "Pick a number"
.Flags = TDF_SLIDER Or TDF_USE_COMMAND_LINKS
.SliderSetRange 0, 100, 10
.SliderSetChangeValues 10, 20
.SliderTickStyle = SldTickStyleBoth
.SliderValue = 50
.SliderAlign = TDIBA_Content
.ExpandedControlText = "ExpandMe"
.ExpandedInfo = "Expanded"
.AddCustomButton 100, "CommandLink"
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
|
https://www.vbforums.com/attachment....hmentid=194452 |
Finally, here's an example of using 3 of the new controls together in combination with two of the built-in controls, as well as the option to replace the shield icon with one from shell32.dll, while maintaining the gradient background.
Code:
himlSys = GetSystemImagelist(SHGFI_SMALLICON)
Dim hIconF As Long
hIconF = IconToHICON(LoadResData("ICO_CLIP", "CUSTOM"), 16, 16)
With TaskDialog1
.Init
.MainInstruction = "Schedule Event"
.Content = "Pick action to schedule. Provide a date, and optionally a specific time. You can also set a name below."
.Flags = TDF_DATETIME Or TDF_INPUT_BOX Or TDF_COMBO_BOX Or TDF_USE_HICON_FOOTER Or TDF_USE_SHELL32_ICONID Or TDF_KILL_SHIELD_ICON Or TDF_USE_COMMAND_LINKS
.DateTimeType = dttDateTimeWithCheckTimeOnly
.DateTimeAlign = TDIBA_Buttons
.ComboAlign = TDIBA_Content
.ComboStyle = cbtDropdownList
.ComboSetInitialItem 1
.ComboImageList = himlSys
.ComboAddItem "Do Thing #1", 2
.ComboAddItem "Do Thing #2", 7
.ComboAddItem "Do Thing #3", 8
.CommonButtons = TDCBF_CANCEL_BUTTON
.InputText = "New Event 1"
.InputAlign = TDIBA_Footer
.IconMain = TD_SHIELD_GRADIENT_ICON
.IconFooter = hIconF
.IconReplaceGradient = 276
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.AddCustomButton 102, "Schedule" & vbLf & "Additional information here."
.AddRadioButton 110, "Apply to this account only."
.AddRadioButton 111, "Apply to all accounts."
.ShowDialog
Label2.Caption = "Radio: " & .ResultRad
Label5.Caption = .ResultInput
Label7.Caption = .ResultComboText
Label9.Caption = .ResultComboIndex
Label11.Caption = .ResultDateTime
If .ResultDateTimeChecked = 0 Then
Label13.Caption = "Time unchecked."
Else
Label13.Caption = "Time checked."
End If
If .ResultMain = 102 Then
Label1.Caption = "Scheduled."
Else
Label1.Caption = "Cancelled."
End If
End With
|
https://www.vbforums.com/attachment....hmentid=194453 |
Finally Version 1.0 added high DPI support and a few extra features.
When creating the dialog, you specify an auto-close time in seconds. When you retrieve the property while the dialog is active, it returns the time remaining, allowing you to do things like tie it into a progress bar of doom:
Code:
With TaskDialogAC
.Init
.MainInstruction = "Do you wish to do somethingsomesuch?"
.Flags = TDF_CALLBACK_TIMER Or TDF_USE_COMMAND_LINKS Or TDF_SHOW_PROGRESS_BAR
.Content = "Execute it then, otherwise I'm gonna peace out."
.AddCustomButton 101, "Let's Go!" & vbLf & "Really, let's go."
.CommonButtons = TDCBF_CLOSE_BUTTON
.IconMain = IDI_QUESTION
.IconFooter = TD_ERROR_ICON
.Footer = "Closing in 15 seconds..."
.Title = "cTaskDialog Project"
.AutocloseTime = 15 'seconds
.ParenthWnd = Me.hwnd
.ShowDialog
End With
'Then:
Private Sub TaskDialogAC_DialogCreated(ByVal hwnd As Long)
TaskDialogAC.ProgressSetRange 0, 15
TaskDialogAC.ProgressSetState ePBST_ERROR
End Sub
Private Sub TaskDialogAC_Timer(ByVal TimerValue As Long)
On Error Resume Next
TaskDialogAC.Footer = "Closing in " & TaskDialogAC.AutocloseTime & " seconds..."
TaskDialogAC.ProgressSetValue 15 - TaskDialogAC.AutocloseTime
On Error GoTo 0
End Sub
When a dialog times out, it returns a TD_CANCEL main result (it also does this if you hit 'x'). |
https://www.vbforums.com/attachment....hmentid=194454 |
Next up, logo images. Now, there's a very wide variety of image processing techniques. Rather than accept a file name and limit things to one method, the class module must simple be passed an HBITMAP, which you can acquire in a multitude of ways. The demo project uses GDI+, so a generic set of standard images is supported. The logo image can go in two places, in the buttons position (means no controls, custom or built-in, can be placed there):
Code:
'some initializing settings from the picture are omitted
Dim hBmp As Long
Dim sImg As String
sImg = App.Path & "\vbf.jpg"
Dim cx As Long, cy As Long
hBmp = hBitmapFromFile(sImg, cx, cy)
With TaskDialog1
.Init
'[...]
.SetLogoImage hBmp, LogoBitmap, LogoButtons
.ShowDialog
End With
Call DeleteObject(hBmp) 'do not free image until the dialog is closed, otherwise it won't show.
|
https://www.vbforums.com/attachment....hmentid=194457 |
Two more features of the logos are support for transparency, and support for custom offsets from the edges. Transparency, due to the way TaskDialogs work with that infernal DirectUIHWND, require some processing... the background color isn't correctly reported so it's transparent to the wrong color. This is resolved by custom-setting the background it's transparent to to the current RBG of the actual visible pixels. If we always just used the standard window background, it wouldn't work with the shield gradients. Don't worry, it's all handled for you. Just set the alignment and desired offsets:
Code:
.SetLogoImage hBmp, LogoBitmap, LogoTopRight, 4, 4
(as suggested by 'LogoBitmap', there's also 'LogoIcon' that you need to use for .ico's) |
https://www.vbforums.com/attachment....hmentid=194456 |
Finally we get to drop down buttons. It's a pretty rare thing to require, but once subclassing had to be brought in for a host of other uses, why not add it? Note that the demo project prints a debug message when the dropdown arrow is clicked, I didn't include the large amount of code to then generate a menu at that spot. If you need help with doing that, let me know in this thread or by PM, as I do have that code written.
At this time, it's only possible to make a split button out of a custom button; which has to be a normal button-- so command links aren't supported. Turning a button into a split button is done by ID (and only 1 button can be made into a split one at this time):
Code:
.AddCustomButton 123, " SuperButton "
.SetSplitButton 123
Note the extra space padding-- this is required for the button to look right. I'll look into automating the addition of spaces in the future but for now it's gotta be manually done.
Icons work but aren't perfect:
https://www.vbforums.com/attachment....hmentid=194458
You might want to consider customizing the icon by having some blank pixels on the left.
|
https://www.vbforums.com/attachment....hmentid=194459 |
See post #4 for events, properties, and methods
Thanks
Thanks very much to LaVolpe for helping me get the callbacks hyperlinks going with this project. Also Julius Laslo's TaskDialogIndirect implementation for the delcares/constants and the general principle of calling this monster of a function; and Randy Birch's simple TaskDialog implementation for getting me started.
---
Last updated 20 Feb 2026
Feel free to do whatever you want with this code, all I ask is you provide credit to me as I have done with others.
All bug reports, comments, criticisms, and ideas on how to improve the project are absolutely welcome!
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
fafalone
All bug reports, comments, criticisms, and ideas on how to improve the project are absolutely welcome!
The TaskDialogCallbackProc callback function can be reduced and made more efficient at the same time by simply modifying the type of its last parameter:
Code:
Public Function TaskDialogCallbackProc(ByVal hWnd As Long, ByVal uNotification As Long, ByVal wParam As Long, ByVal lParam As Long, ByVal lpRefData As cTaskDialog) As Long
''***DO NOT CALL THIS FUNCTION***
TaskDialogCallbackProc = lpRefData.ProcessCallback(hWnd, uNotification, wParam, lParam)
End Function
The ProcessCallback's arguments are best passed ByVal because passing pointers (ByRef) to Longs are slightly slower than passing them by value.
InitCommonControlsEx's return value should be Long instead of Boolean (the incorrect declaration doesn't appear to cause any problems, though).
Quote:
Originally Posted by
fafalone
... but the url needs to be in quotes, so you'll need chr$(34).
Double quotes can be embedded in a string literal by escaping it with another double quote. This avoids potentially expensive string concatenations:
Code:
.Content = "Where else to link to but <a href=""http://www.microsoft.com"">Microsoft.com</a>"
Quote:
Originally Posted by
fafalone
It gets around VB's limitations on icons by adding them to the resource file as a custom resource, and not an icon. This way, you can include any size and any color depth and any number of them inside the .ico.
. . .
Due to the severe limitations on what icons can be put in a VB project res file in the actual icon group, ...
Actually, the problem lies with the default resource compiler that came with VB6 (RC.EXE). It doesn't understand the latest icon formats, such as the 256x256 PNG-compressed 32bpp "Heart" icon in your resource file. By using a newer version of RC.EXE (such as the one from the Vista (or later) SDK), it now becomes possible to include proper icon resources that take advantage of the latest icon formats.
Quote:
Originally Posted by
fafalone
Then, the ResIcontoHICON function will give you the hIcon you need for cTaskDialog. But remember, any other function returning an hIcon will work.
Don't forget to destroy those hIcons too! Or else, each time ResIconTohIcon() is called with the same resource ID, the previously created icon from that resource ID gets leaked!
Quote:
Originally Posted by MSDN
Quote:
Originally Posted by
fafalone
.ParenthWnd - May or may not matter; I haven't had any problems not setting it.
Despite its name, the hwndParent member of the TASKDIALOGCONFIG structure specifies the owner window of the task dialog. It is akin to invoking .Show vbModal, Me.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
ADDITIONAL DOCUMENTATION
Here's a full list of the flags, public events, properties and methods:
Full Class Call List
Flags
| TDF_ENABLE_HYPERLINKS |
Allows hyperlinks in standard form. |
| TDF_USE_HICON_MAIN |
Required to use a custom icon. |
| TDF_USE_HICON_FOOTER |
Required to use a custom icon. |
| TDF_ALLOW_DIALOG_CANCELLATION |
Adds an X in the top right and a copy of the main icon to the titlebar. |
| TDF_USE_COMMAND_LINKS |
|
| TDF_USE_COMMAND_LINKS_NO_ICON |
|
| TDF_EXPAND_FOOTER_AREA |
When expanded info is shown, it's placed at the bottom. |
| TDF_EXPANDED_BY_DEFAULT |
Expanded information is shown by default and the button shows its expanded state. |
| TDF_VERIFICATION_FLAG_CHECKED |
Makes the checkbox checked by default. |
| TDF_SHOW_PROGRESS_BAR |
Shows a regular progress bar. |
| TDF_SHOW_MARQUEE_PROGRESS_BAR |
Use ProgressStartMarquee |
| TDF_CALLBACK_TIMER |
Sends the Timer event every 200ms |
| TDF_POSITION_RELATIVE_TO_WINDOW |
Center on ParenthWnd instead of screen. |
| TDF_RTL_LAYOUT |
Right-to-left layout. |
| TDF_NO_DEFAULT_RADIO_BUTTON |
|
| TDF_CAN_BE_MINIMIZED |
|
| TDF_NO_SET_FOREGROUND |
|
| TDF_USE_SHELL32_ICONID |
(Custom flag) Icon is loaded from shell32.dll |
| TDF_USE_IMAGERES_ICONID |
(Custom flag) Icon is loaded from imageres.dll |
| TDF_USE_SHELL32_ICONID_BUTTON |
(Custom flag) Button icon is loaded from shell32.dll |
| TDF_USE_IMAGERES_ICONID_BUTTON |
(Custom flag) Button icon is loaded from imageres.dll |
| TDF_EXEC_HYPERLINKS |
(Custom flag) Automatically launch link as-is with explorer (uses default browser for web) |
| TDF_USE_SHELL32_ICONID_BUTTON |
(Custom flag) IconMain/IconFooter specify an icon id in shell32.dll |
| TDF_USE_IMAGERES_ICONID_BUTTON |
(Custom flag) Same, but with imageres.dll |
| TDF_KILL_SHIELD_ICON |
(Custom flag) Allows the colored background from the TD_SHIELD_x icons to be used with just the text and no shield icon |
| TDF_INPUT_BOX |
(Custom flag) Adds an Input box control. |
| TDF_COMBO_BOX |
(Custom flag) Adds Combo box; .ComboType controls edit or list |
| TDF_DATETIME |
(Custom flag) Adds date control, time control, or both |
| TDF_SLIDER |
(Custom flag) Adds Slider control |
Events
| DialogCreated(hWnd As Long) |
Occurs when the dialog becomes visible. Returns dialog's hWnd, also available via .hWndDlg |
| ButtonClick(ButtonID As Long) |
|
| HyperlinkClick(lPtr As Long) |
Contains a pointer to a string containing the URL passed through the callback lParam. |
| Timer(TimerValue As Long) |
Every 200ms if flag set; value contains millisecs since created or timer reset |
| DialogDestroyed() |
|
| RadioButtonClick(ButtonID As Long) |
|
| DialogConstucted(hWnd As Long) |
Occurs after the dialog is created in memory, but before it's shown to the user |
| VerificationClicked(Value As Long) |
0=unchecked, 1=checked |
| ExpandButtonClicked(Value As Long) |
0=collapsed, 1=expanded |
| Help() |
Triggered by the user pressing F1 in the dialog. |
| DropdownButtonClicked(ByVal hwnd As Long) |
(Custom event) Fired when a user clicks the arrow of a split button made with .SetSplitButton |
| ComboItemChanged(ByVal iNewItem As Long) |
(Custom event) For use with the Combo Box custom control |
| ComboDropdown() |
(Custom event) For use with the Combo Box custom control |
| InputBoxChange(sText As String) |
(Custom event) For use with the Input Box custom control |
| DateTimeChange(ByVal dtNew As Date, ByVal lCheckStatus As Long) |
(Custom event) For use with the date/time controls |
| SliderChange(ByVal lNewValue As Long) |
(Custom event) For use with the slider custom control |
| AutoClose() |
(Custom event) Fired when the dialog is closed in response to a .AutoCloseTime set timeout |
Properties
| .AutocloseTime |
Get/Let, Modify if open |
Specifies, in seconds, a timeout after which the dialog automatically closes. While running, retrieves the time remaining. |
| .CollapsedControlText |
Get/Let |
For the text next to the round expando control while not expanded |
| .ComboAlign |
Get/Let |
The position of the combo box, in the content area, by the buttons, or in the footer |
| .ComboAlignInFooter |
Get/Let |
For combo boxes in the footer, left-center-right alignment |
| .ComboCueBanner |
Get/Let, Modify if open |
The cue banner text for editable combo boxes |
| .ComboDropWidth |
Get/Let, Modify if open |
Width of the dropdown list. |
| .ComboHeight |
Get/Let |
The height of the dropdown list of the combo control. Default is 115px (DPI scaled) |
| .ComboImageList |
Get/Let |
Associates the combo control with an HIMAGELIST (API/COM imagelists) |
| .ComboIndex |
Get/Let, Modify if open |
The currently selected combo item |
| .ComboOffsetX |
Get/Let |
Manual adjustment for position offset. |
| .ComboStyle |
Get/Let |
Normal dropdown (editable) or dropdown list |
| .ComboText |
Get |
The current text of the combo box. |
| .ComboWidth |
Get/Let |
Override the automatic width value |
| .CommonButtons |
Get/Let |
For the standard OK, Cancel, Yes, No, etc buttons |
| .Content |
Get/Let, Modify if open |
The primary message text |
| .DateTimeAlign |
Get/Let |
Date/Time control position (content, buttons, footer) |
| .DateTimeAlignInButtons |
Get/Let |
If in content area, left-right-center alignment |
| .DateTimeAlignInContent |
Get/Let |
If in content area, left-right-center alignment |
| .DateTimeAlignInFooter |
Get/Let |
If in footer area, left-right-center alignment |
| .DateTimeChecked |
Get/Let, Modify if open |
A value* representing the current check state; can be used to set default state |
| .DateTimeOffsetX |
Get/Let |
Manual adjustment for position offset. |
| .DateTimeType |
Get/Let |
Date, Time, or Date+Time with checkbox options |
| .DateTimeValue |
Get/Let, Modify if open |
The current date and/or time, as a Date type |
| .DefaultButton |
Get/Let |
Specifies the default button by ID; can be either a common button or custom button ID |
| .DefaultCustomControl |
Get/Let |
If multiple custom controls are present, override the automatic calculation of which one gets focus |
| .DefaultRadioButton |
Get/Let |
The ID of the default radio button |
| .DPIScaleX |
Get/Let |
The current X scaling factor; will be 1 for 100% DPI |
| .DPIScaleY |
Get/Let |
The current Y scaling factor; will be 1 for 100% DPI |
| .ExpandedControlText |
Get/Let |
The text next to the round expando button when expanded |
| .ExpandedInfo |
Get/Let, Modify if open |
The extra information shown/hidden by the expando control |
| .Flags |
Get/Let |
The TDF_x control flags |
| .Footer |
Get/Let, Modify if open |
The text appearing in the footer area |
| .hInst |
Get/Let |
hInstance; usually set by the class automatically, but if you want to change it to another app/dll, you can then specify an icon from that file. Do not set any icon related flags if you do this. The shell32/imageres feature handles this on its own. |
| .hWndCombo |
Get |
The handle of the combo control if present |
| .hWndComboEdit |
Get |
The handle of the combo control's textbox if present |
| .hWndDateTime |
Get |
The handle of the date/time control if present |
| .hWndDlg |
Get |
The main handle of the dialog |
| .hWndDUI |
Get |
The handle of the DirectUIHWND class that holds the controls; immediate parent of custom controls |
| .hWndInput |
Get |
The handle of the input textbox if present |
| .hWndSlider |
Get |
The handle of the slider control if present |
| .IconFooter |
Get/Let, Modify if open |
The ID or HICON for the footer icon |
| .IconMain |
Get/Let, Modify if open |
The ID or HICON for the main icon |
| .IconReplaceGradient |
Get/Let, Modify if open |
If TDF_KILL_SHIELD_ICON is present, you can optionally replace it instead of leaving it gone |
| .InputAlign |
Get/Let |
The position of the input textbox, content-buttons-footer |
| .InputAlignInFooter |
Get/Let |
If in the footer, left-right-center alignment |
| .InputCueBanner |
Get/Let, Modify if open |
The cue banner text of the input textbox |
| .InputIsPassword |
Get/Let, Modify if open |
Set/unset the password style that hides the text behind asterisks/dots |
| .InputOffsetX |
Get/Let |
Manual adjustment for position offset. |
| .InputText |
Get/Let, Modify if open |
The current text of the input textbox; can be used to set default text |
| .InputWidth |
Get/Let |
Override the automatic width value of the input textbox |
| .MainInstruction |
Get/Let, Modify if open |
The larger header text above the main message text |
| .ParenthWnd |
Get/Let |
Not mandatory, but if set the dialog will appear modally as a child of the given for (like Form.Show vbModal, Me) |
| .ResultComboIndex |
Get |
The final item selected when the dialog closes |
| .ResultDateTime |
Get |
The final date and/or time when the dialog closes |
| .ResultDateTimeChecked |
Get |
The final check status of date/time control(s) when the dialog closes |
| .ResultInput |
Get |
The final text of the input textbox when the dialog closes |
| .ResultMain |
Get |
The ID of the button that closed the dialog. TDCANCEL is also the result for the 'X' button closing the dialog or the Autoclose timeout being reached |
| .ResultRad |
Get |
The ID of the radio button selected when the dialog closes |
| .ResultVerify |
Get |
The status of the verification checkbox when the dialog closes |
| .SliderAlign |
Get/Let |
The position of the slider control, content-buttons-footer |
| .SliderAlignInFooter |
Get/Let |
If the slider is in the footer, left-right-center alignment |
| .SliderOffsetX |
Get/Let |
Manual adjustment for position offset. |
| .SliderTickStyle |
Get/Let |
Set if ticks appear on the top, bottom, or both |
| .SliderValue |
Get/Let, Modify if open |
The current value of the slider; can be used to set the default value |
| .SliderWidth |
Get/Let |
Override the automatic width value of the slider control |
| .Title |
Get/Let |
Main dialog title appearing in the control bar |
| .VerifyText |
Get/Let |
Text appearing next to the Verify checkbox |
| .Width |
Get/Let |
You can manually specify the width of the TaskDialog in Dialog Units (which are not pixels or twips). If not set, Windows automatically calculates an optimal value. |
Methods
| Method |
Effects open dlg? |
Description |
| .AddCustomButton(id, text [,hIcon]) |
No |
Adds a custom button. These appear as command links if enabled, otherwise as standard buttons |
| .AddRadioButton(id, text) |
No |
Adds a radio button |
| .ClearCustomButtons |
No |
Clears the current set of custom buttons |
| .ClearRadioButtons |
No |
Clears the current set of radio buttons |
.ClickButton(id)
.ClickRadioButton(id) |
Yes |
Click a button by id |
| .ClickVerification(state[,focused]) |
Yes |
0/1 for checked, 1 to set keyboard focus on the checkbox |
| .CloseDialog |
Yes |
Closes an open dialog and returns TD_CLOSE as the result, even if that button is not present. |
| .ComboAddItem(text[,image][,overlay]) |
Yes |
Adds an item to the combo control, if present |
| .ComboSetCurrentState(text[,image][,overlay]) |
Yes |
Sets the text/image of the edit box of a combo control |
| .ComboSetInitialItem(idx) |
No |
Sets the default combo item |
| .ComboSetInitialState(text[,image][,overlay) |
No |
The default text/image of the combo edit box |
| .DateTimeSetInitial(date) |
No |
The default for the date and/or time control |
| .DateTimeSetRange(setmin,setmax,min,max) |
Yes |
Limit the dates/times allowed |
.EnableButton(id, state)
.EnableRadioButton(id, state) |
Yes |
Enable/disable a button by id (0/1) |
| .GetCustomButtons(ids(),strs()) |
No |
Get an array of the current custom buttons |
| .GetRadioButtons(ids(),strs()) |
No |
Get an array of the current radio buttons |
| .Init |
No |
Resets all config settings to their default state |
| .NavigatePage(cTaskDialog) |
Yes |
Mutli-page navigation; opens a new taskdialog as a page of the current one |
.ProcessCallback
.ProcessEnumCallback
.ProcessSubclass |
These are public but for internal use; DO NOT CALL |
| .ProgressSetRange(min,max) |
Yes |
Sets the range of the progress bar |
| .ProgressSetState(state) |
Yes |
Progress state; green/normal, yellow/pause, red/error |
| .ProgressSetType(type) |
Yes |
Sets regular or marquee type progress bar |
| .ProgressSetValue(value) |
Yes |
Sets the current progress value |
.ProgressStartMarquee(speed)
.ProgressStopMarquee |
Yes |
Start/stop a marquee-style progress bar |
| .ResetTimer |
Yes |
Resets the value sent by the Timer event. |
| .SetButtonElevated(buttonid,state) |
Yes |
Adds the security shield icon to a button to indicate elevated user permissions are required to perform that action. 1 to turn on, 0 to turn off the icon. |
.SetButtonHold(buttonid)
.ReleaseButtonHold |
Yes |
Places a hold on a button: the dialog will not close when the button is clicked. The click event is still sent. |
| .SetCommonButtonIcon(tdbutton,hicon) |
No |
Add an HICON-based icon to a common button |
| .SetLogoImage(handle,type,pos[,offX][,offY]) |
No |
Add a logo image in the type right or by the buttons |
| .SetSplitButton(id) |
No |
Add a dropdown arrow to a custom button |
| .SetWindowsButtonIconSize(size) |
No |
Set the size of button icons loaded from shell32 or imageres. |
| .ShowDialog |
n/a |
The main command to start the dialog, called after all initial setup |
| .SimpleDialog(text[,btns][,title][,maintxt][,icon][,hwnd][,hinst]) |
n/a |
Shows a rudimentry task dialog with the plain TaskDialog() API |
| .SliderSetChangeValues(small,large) |
Yes |
The value of a small and large change in the slider control |
| .SliderSetRange(min,max[,tickfqr]) |
Yes |
Range and tick frequency for the slider control |
* - Date/Time checks values are as follows: If there only a single control, or both but only one has a checkbox, it's just 0/1 for unchecked/checked. For 2 controls both with checkboxes, 4=both are checked, 3=date checked, time unchecked, 2=date unchecked, time checked, 0=neither are checked
Recycling this post position for overrun from the lead posts; it was originally a comment reading:
Even if you used a newer RC, wouldn't you still have to insert the icon outside of the built-in resource editor?
Thanks for the tips, I'm incorporating them in now.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
fafalone
Even if you used a newer RC, wouldn't you still have to insert the icon outside of the built-in resource editor?
Unfortunately, yes. I tried replacing the older RC.EXE that came with VB6 with the one from the Win 7 SDK, but still, VB6's Resource Editor add-in complains that your icons are "Invalid Icon Files". I'm not aware of any replacement for the default VB6 Resource Editor add-in, so you'll have to manually create a resource file yourself using the latest RC.EXE if you want to properly embed modern icons in your compiled binaries. It's not too difficult anyway. You just have to write a resource script like this:
Code:
// td.rc
#define ICO_CLOCK 101
#define ICO_HEART 102
ICO_CLOCK ICON Clock.ico
ICO_HEART ICON Heart.ico
#define MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
MANIFEST_RESOURCE_ID RT_MANIFEST taskdialogindirect.exe.manifest
Then compile it via the command line or with a batch file, such as this:
Code:
:: CompileRC.cmd
@CD /D %~dp0
:: "%ProgramFiles%\Microsoft Visual Studio\VB98\Wizards\RC.EXE" td.rc
"%ProgramFiles%\Microsoft SDKs\Windows\v7.0\Bin\RC.Exe" /nologo td.rc
@PAUSE > NUL
Finally, manually add the resource file to your VB6 project. That's all.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
So, in other words, adding it as a custom resource and using the ResIconToHICON function is not only much easier, but the only way to go if you want to use it while in IDE? :o
But yeah editing the resource manually is still really useful... I do it to add custom version information and would really like to experiment with providing a dialog template for functions like GetOpenFileName.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
fafalone
So, in other words, adding it as a custom resource and using the ResIconToHICON function is not only much easier, but the only way to go if you want to use it while in IDE? :o
It's also possible to obtain a handle to an icon resource (even in the IDE) via LoadResPicture(IconID, vbResIcon).Handle. However, in cases where it refuses to load the newer icon formats, the following (IDE-friendly) alternative to ResIconTohIcon() should do the trick:
Code:
Option Explicit 'In a standard module
Private Declare Function CreateIconFromResourceEx Lib "user32.dll" ( _
ByRef pbIconBits As Any, _
ByVal cbIconBits As Long, _
Optional ByVal fIcon As Long = -True, _
Optional ByVal dwVersion As Long = &H30000, _
Optional ByVal cxDesired As Long, _
Optional ByVal cyDesired As Long, _
Optional ByVal uFlags As Long _
) As Long
Private Declare Function LoadImageW Lib "user32.dll" ( _
ByVal hInst As Long, _
ByVal lpszName As Long, _
Optional ByVal uType As Long, _
Optional ByVal cxDesired As Long, _
Optional ByVal cyDesired As Long, _
Optional ByVal fuLoad As Long _
) As Long
Private Declare Function LookupIconIdFromDirectoryEx Lib "user32.dll" ( _
ByRef presbits As Any, _
Optional ByVal fIcon As Long = -True, _
Optional ByVal cxDesired As Long, _
Optional ByVal cyDesired As Long, _
Optional ByVal Flags As Long _
) As Long
Public Declare Function DestroyIcon Lib "user32.dll" (ByVal hIcon As Long) As Long
Code:
'ID - Integer, Long or String identifier of the icon resource. Any other Variant subtype will raise an error.
'Width - The desired width, in pixels, of the icon. If not supplied or 0, the function uses the SM_CXICON system metric value.
'Height - The desired height, in pixels, of the icon. If not supplied or 0, the function uses the SM_CYICON system metric value.
'GethIconFromResID - Returns a handle to the loaded/created icon if successful, 0 or an error otherwise. Don't forget to destroy the icon when done!
Public Function GethIconFromResID(ByRef ID As Variant, Optional ByVal Width As Long, Optional ByVal Height As Long) As Long
Const IMAGE_ICON = 1&, LR_DEFAULTSIZE = &H40&, RT_GROUP_ICON = 14&, RT_ICON = 3&
Dim Data() As Byte
If App.LogMode Then
GethIconFromResID = LoadImageW(App.hInstance, Choose(VarType(ID), , ID, ID, , , , , StrPtr(ID)), IMAGE_ICON, Width, Height, LR_DEFAULTSIZE)
Else
Data = LoadResData(ID, RT_GROUP_ICON)
Data = LoadResData(LookupIconIdFromDirectoryEx(Data(0&), , Width, Height), RT_ICON)
GethIconFromResID = CreateIconFromResourceEx(Data(0&), UBound(Data) + 1&, , , Width, Height)
End If
End Function
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
A couple of points
1) Nothing against the code Bonnie posted. But if this requires a typical user to manually use rc.exe, may not find many takers
2) Regarding this TaskDialog (your implementation or anyone's). Have you tested it with multiple forms open? Wondering if you get the same thing I do. We know a msgbox is application modal by nature. You open multiple forms & display a msgbox, you aren't going to access any of those forms until that msgbox is closed. Not so with the TaskDialog
P.S. this horizontal scrolling is annoying; maybe it's just my browser
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Are you saying that if you made it modal with Form1 as the parent, you wouldn't be able to access Form2? If so, that's not the case with my implementation. If .ParenthWnd is set, that form becomes inaccessible, but other forms respond:
http://i.imgur.com/kIDHI82.jpg
With ParenthWnd=me.hwnd, i clicked the first dialog, then clicked form2 and the msgbox came up fine. If ParenthWnd=0, then all forms, including the calling form, are available.
Quote:
It's also possible to obtain a handle to an icon resource (even in the IDE) via LoadResPicture(IconID, vbResIcon).Handle
But if you could get it into the icon group, you'd just have to set hInst=App.hInstance and .IconMain=IconID anyway; no need to worry about an hIcon for it.
Quote:
P.S. this horizontal scrolling is annoying; maybe it's just my browser
Do you mean my new readme post format? I set the table width to 100% and not a fixed width so it should work for most resolutions... although it does look like the comments themselves are fixed-width.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Are you saying that if you made it modal with Form1 as the parent, you wouldn't be able to access Form2? If so, that's not the case with my implementation. If .ParenthWnd is set, that form becomes inaccessible, but other forms respond
The opposite, I was saying exactly what you said. Unlike the msgbox, the TaskDialog is NOT application-modal
Quote:
Do you mean my new readme post format?
No, I was talking about this thread. Gotta scroll way right/left to see read the entire posts
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
FYI. I was playing around with trying to make the TaskDialog application-modal, or at least appear application modal.
Here's a method that appears to work (for most cases).
1) Locate the project owner window. In VB all forms appear to be owned by a hidden 'master' VB window, both in design and when compiled.
This master window will be the owner (GetWindow hWnd, GW_OWNER) for each form in the project, not already owned by an existing project window
This master window will not have an owner itself
2) Once the master is found, call EnableWindow on its handle to disable it, disables the owned windows too.
So, I locate the master window, disable it, call task dialog. None of the windows (other than the TaskDialog) are accessible. After TaskDialog closes, renable the master window.
For fun, one does not need to be passed a parent hWnd for the TaskDialog to make the above work. I have a routine that will
1) Enumerate the desktop windows
2) Find the first visible window having the same thread ID as the project. This will be the parent window for TaskDialog
3) Locate the master from that window's hWnd
Now I can disable it & make the TaskDialog appear application-modal. Of course one can offer options: app-modality, no-modality, modality for calling form only
Oh, I forgot to mention a significant point with the above logic. While uncompiled, that master window owns the IDE too; i.e., IDE disabled :D
Though with a msgbox, you can't access the code while it is displayed either... So, in another case, imitates msgbox modality well.
For debugging purposes, if this method is used, it is very important that ppl understand that just like the msgbox, this method would block RaiseEvent calls while uncompiled. So, making the TaskDailog imitate msgbox modaility, you get the downsides too. Of course, this all can be worked around too by simply not permitting application modality during IDE or providing a property that will enable/disable this functionality while in IDE. For the TaskDialog, blocked RaiseEvent calls only an issue if the TaskDialog is calling back to a class and the class uses RaiseEvent to inform the host what messages were received from the TaskDialog.
Again, this modality is only an issue in IDE and in a specific scenario: need callbacks. And I can see this as being a major issue if the user needs to respond to adjust the progressbar, respond to URL clicks, respond to TaskDialog button clicks, etc. Being able to respond to these things make the TaskDialog more advantageous than the msgbox. So, how do we get IDE modality and prevent RaiseEvents from being blocked while in IDE? Answer is quite simple. Use of an Implementation class as those are not blocked. This method resolves the issue and requires reformating a class that uses RaiseEvents. The form/uc/propertypage/class that calls the TaskDialog class must use keyword Implements the new callback class. That callback class uses Public methods that replicate the RaiseEvent's Public Events. Instead TaskDialog class would need a property/parameter accepting the calling code as an instance of that callback class. Now instead of RaiseEvent, you would trigger the callback class's public methods. Short, incomplete sample follows:
Callback class: ITaskDialogCallback.
Code:
Public Sub HyperlinkClicked(ByVal hWndTDialog As Long, ByVal URL As String)
End Sub
... other public methods as needed
The TaskDialog class: cTDialog. This is the class that has all the dialog properties & the ShowDialog method
Code:
Private m_Caller As ITaskDialogCallback ' class level variable
Public Property Set Caller(theCaller As ITaskDialogCallback) ' method to set caller, optionally can be a parameter in the ShowDialog routine?
Set m_Caller = theCaller
End Property
... within the method that receives the task dialog callback messages
If m_Caller Is Nothing Then Exit Function
Select Case uNotification
Case TDN_HYPERLINK_CLICKED Then
' get the URL from the provided string pointer
Call m_Caller.HyperlinkClicked(hWnd, strURL)
... other case statements
End Select
' reset m_Caller on the TDN_DESTROYED notification
The calling form:
Code:
Implements ITaskDialogCallback
' we'll say that an instance of the cTDialog class is named: clsTDialog
Set clsTDialog.Caller = Me
clsTDialog.ShowDialog
....
' And the Implementation routine (added automatically by VB) looks like
Private Sub ITaskDialogCallback_HyperlinkClicked(ByVal hWndTDialog As Long, ByVal URL As String)
' send url to default browser
End Sub
Side note. Haven't tested this, modality-workaround, with MDI forms, but don't expect different behavior
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
LaVolpe
Unlike the msgbox, the TaskDialog is NOT application-modal
Indeed. The MessageBox function has an MB_TASKMODAL flag whereas TaskDialogIndirect has no such equivalent.
Quote:
Originally Posted by MSDN
MB_TASKMODAL
0x00002000L |
Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle available but still needs to prevent input to other windows in the calling thread without suspending other threads. |
Message Boxes
A message box is a modal dialog box and the system creates it by using the same internal functions that
DialogBox uses. If the application specifies an owner window when calling
MessageBox or
MessageBoxEx, the system disables the owner.
An application can also direct the system to disable all top-level windows belonging to the current thread by specifying the MB_TASKMODAL value when creating the dialog box.
Quote:
Originally Posted by
LaVolpe
I have a routine that will
1) Enumerate the desktop windows
. . .
Here's another approach that doesn't require a callback procedure.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
Bonnie West
Here's[/URL] another approach that doesn't require a callback procedure.
Appreciated. I don't use EnumWindows API, just imitate it with: FindWindow, GetWindowThreadProcessId & GetNextWindow calls in a loop
But I see I can streamline it by using Forms collection - didn't even consider that!
Edited:
However, after some testing, I'll keep my current method. As it exists, when a task dialog is displayed on top of another task dialog, the current method recognizes the previous dialog as top most in the thread even though it doesn't have a ThunderMain class. But using the Forms collection would allow me to jump right to an hWnd in the thread vs starting with a random desktop window & navigating from there. Worth tinkering with. Thanx
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Note- project has been updated- if you're using the multi-page feature, the new version has very important bugfixes. Otherwise updates not of particular importance. See details in first post.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Bug report:
Code:
Public Property Let Width(Value As Long): uTDC.cxWidth = Width: End Property
Should be:
Code:
Public Property Let Width(Value As Long): uTDC.cxWidth = Value: End Property
Users should probably be reminded that the width of the task dialog's client area is in dialog units, not pixels, twips or anything else.
Quote:
Originally Posted by
fafalone
| (v0.5.1) |
Added a routine to force the case of the Enum's for easier development. |
You might be interested in this subroutine that automates redeclaring an Enum's members. ;)
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Well that's embarrassing. Thanks... file updated.
Dialog units sure look like an unmitigated pain in the a**... MapDialogRect() and GetDialogBaseUnits() both require the hWnd of the dialog, and .width is set beforehand. And methods to calculate it without that seem a bit involved. For now I'll just note it in the readme; but I'd really like to just accept it in pixels and/or twips. Is there a simple way I'm not seeing?
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
fafalone
Is there a simple way I'm not seeing?
Sorry, but I'm not aware of any. I believe, however, that it's better to use dialog units because it is a device-independent measurement. The task dialog will be able to retain its proportions regardless of the current screen DPI.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
Bonnie West
I believe, however, that it's better to use dialog units because it is a device-independent measurement...
I can see fafaone's issue. If offering dialog units as a measurement, you don't know what those units are beforehand as they are dependent on the dialog's font. If TaskDialog uses the system font, then it's easy enough with GetDialogBaseUnits API. However, per msdn, most dialogs define their own font & there is no guarantee that the same font will be used from O/S to O/S.
A simple idea if wanting to provide users a property for pixels & simple enough to test:
During the dialog's creation, a couple of notifications are received: tdn_created & tdn_dialog_constructed.
When seeing one of those, maybe a simple call to SetWindowPos API would do the trick.
Of course the dialog structure's width member would need to be cached & not passed to TaskDialog API. The cached value used for SetWindowPos
Note. That the structure's width member is not only intended to be dialog units, but also client area only
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
fafalone... just a heads up regarding my last post
Though SetWindowPos works well for sizing the window, the dialog messages come too late. The guts of the dialog is drawn to the size before SetWindowPos was called.
So, a new train of thought is required.
1) Probably can inform users that the Width property requires Dialog units, but for most users, might as well just not offer the property
2) If the width property is set, could potentially create a generic TaskDialog window, in the tdn_created event destroy the window, but before that:
-- call MapDialogRect to convert a 100x100 rectangle so you can get the unit-to-pixel ratio
-- cache that ratio & use it to convert user-supplied pixel value to dialog value for the TDialog structure's width member
-- Downside is that if caching this, then should user change themes while project is alive, the ratio will probably no longer work
:: fix for last statement: subclass for theme changes? perform step 2) above prior to displaying each TaskDialog?
Code:
' example
Private Function pvTaskDialogCallback(ByVal hWnd As Long, ByVal uNotification As Long, _
ByVal wParam As Long, ByVal lParam As Long, ByVal dwRefData As Long) As Long
' for my class, dwRefData is a reference to an Implementation class.
' When -1, it is a flag I set for getting horizontal ratio
Select Case uNotification
Case TDN_CREATED
If dwRefData = -1& Then
Dim tRect As RECT
tRect.Right = 100: tRect.Bottom = 100
MapDialogRect hWnd, tRect
m_HorzRatio = tRect.Right ' cached class-level variable
DestroyWindow hWnd
' user-set pixel width can now be calculated as: (Width * 100) \ m_HorzRatio
Else
...
End If
.... other case statements
End Select
End Function
3) Might be able to create a new window, based on the TaskDialog window class, get the font and calculate your own dialog units. Then destroy that window
-- measurement may not be exactly as windows calculates, but should be close
Quote:
Originally Posted by msdn
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
For a dialog box that does not use the system font, the base units are the average width and height, in pixels, of the characters in the dialog's font. You can use the GetTextMetrics and GetTextExtentPoint32 functions to calculate these values for a selected font. However, by using the MapDialogRect function, you can avoid errors that might result if your calculations differ from those performed by the system.
Each horizontal base unit is equal to 4 horizontal dialog template units; each vertical base unit is equal to 8 vertical dialog template units. to convert from pixels to dialog template units, use the following formulas:
templateunitX = MulDiv(pixelX, 4, baseunitX)
Edited
But in hindsight, since the TaskDialog uses the standard Windows dialog class (#32770), would expect the system font to be used & therefore GetDialogBaseUnits API can be used.
Above being said, the conversion is as simple as the following:
Code:
dialogClientWidth = (desiredWidth * 4&) \ (GetDialogBaseUnits() And &HFFFF&)
In my tests, results of both sample routines returned the same calculated/converted values :bigyello:
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
So I guess the question is, are there any scenarios under which a task dialog wouldn't use the system font?
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
I have not downloaded your project to play with -- still tweaking, working on my own version.
Here's one for you if it applies. In a multipage dialog, progressbars can be added, but in my case, they will not work (show progress) on any page other than the very first one. Same for you? I'm also fine tuning multipage dialogs & expect to find another oddity or two
Edited: Never mind. Figured it out. Just FYI if you run into same situation
Problem
1) I was initializing progressbars & setting control properties after the dialog constructed message received
-- this is not an issue for the 1st page of a dialog
2) There really isn't another message to use after the construction message (other than possibly the TDN_TIMER if it applies)
3) When any other page was later displayed, none of my initialization code seemed to work
Apparent Fix
1) After the constructed message received, set a short timer. I used a 30 ms timer
2) When timer event received, kill timer, then apply the initialization code
Works like a champ. My guess is that when the constructed message received for additional pages, it really isn't completely constructed?
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Yeah I ran into the same kinds of issues with having set things up in the initialization routines.
My solution was to use TDN_NAVIGATED for class-level initializations, then for initializing progress bars and such it has an event associated with it. TDN_NAVIGATED is essentially the same as TDN_CREATED for a new page.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Much better solution -- seems to work just fine
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
fafalone, here's something you may be interested in. A way of extracting icons from the uncompiled resource file.
If res file has icon group, containing multiple versions of icon, then when compiled TaskDialog will extract the best match for its purpose. But in design-time, we can't do that because the res file isn't compiled. Our options are to use LoadResPicture which bites the big one here because it'll extract the best 32x32 icon 99% of the time and/or stretch it if necessary, regardless what size was requested (i.e., 16x16, 48x48, etc).
The code below is a 'rough draft' that I'll probably fine tune, but it works pretty well as-is. Note: You'll need to destroy the hIcon at some point
Code:
' sample call
Dim hIcon As Long
hIcon = LoadResIconIDE("C:\Program Files\Microsoft Visual Studio\VB98\Projects\ico.RES", "FOXY", 16, 16)
If hIcon Then
MsgBox "Extracted icon. Handle = " & hIcon
' destroy hIcon when no longer needed
End If
Now for the LoadResIconIDE method
Code:
Private Const RT_ICON As Long = 3
Private Const RT_GROUP_ICON As Long = 14
Private Declare Function LookupIconIdFromDirectoryEx Lib "user32.dll" (ByVal presbits As Long, ByVal fIcon As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Private Declare Function CreateIconFromResourceEx Lib "user32.dll" (ByVal presbits As Long, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Public Function LoadResIconIDE(FileName As String, ResID As Variant, Width As Long, Height As Long) As Long
Dim fnr As Long, lNid As Long
fnr = FreeFile()
Open FileName For Binary Access Read As #fnr
lNid = pvParseRESfile(fnr, RT_GROUP_ICON, ResID, Width, Height)
If Not lNid = 0& Then
LoadResIconIDE = pvParseRESfile(fnr, RT_ICON, lNid)
End If
Close #fnr
End Function
Private Function pvParseRESfile(FileHandle As Long, ResType As Long, ResID As Variant, Optional Cx As Long, Optional Cy As Long) As Long
' PROTOTYPE RESOURCEHEADER << must always start on a DWord boundary
' DataSize AS DWORD ' bytes of resource data following header, not counting any trailing padding used for alignment
' HeaderSize AS DWORD
' ResType(0) AS WORD ' variable length: numeric id or UnicodeZ string data
' ResName(0) AS WORD ' variable length: numeric id or UnicodeZ string data
' Padding AS WORD ???? ' 0-1 WORDs of padding to DWORD-align next item
' DataVersion AS DWORD
' MemoryFlags AS WORD
' LanguageId AS WORD
' Version AS DWORD
' Characteristics AS DWORD
' END PROTOTYPE
' Padding AS WORD ???? ' 0-1 WORDs of padding to DWORD-align data
' resource data (size of DataSize) follows & must always start on a DWord boundary
Dim b() As Byte, iValue As Integer, lRead As Long
Dim lDataSize As Long, lHdrSize As Long
Dim bIsString As Boolean, bOk As Boolean, sID As String
Const ICRESVER As Long = &H30000
Seek #FileHandle, 1
bIsString = (VarType(ResID) = vbString)
Do Until EOF(FileHandle)
Get #FileHandle, , lDataSize: Get #FileHandle, , lHdrSize
If lHdrSize < 32& Or lDataSize < 0& Then Exit Do
Get #FileHandle, , iValue: lRead = 10&
If iValue = &HFFFF Then
Get #FileHandle, , iValue: lRead = lRead + 2&
If iValue = ResType Then
Get #FileHandle, , iValue: lRead = lRead + 2&
If bIsString Then
If Not iValue = &HFFFF Then
ReDim b(0 To lHdrSize - 31&)
Seek FileHandle, Seek(FileHandle) - 2&
Get #FileHandle, , b()
sID = b() ' << note: may contain extra 2 null bytes (DWord alignment) but StrComp ignores it....
lRead = lRead + LenB(sID) - 2&
If StrComp(sID, ResID, vbTextCompare) = 0 Then
bOk = True
Exit Do
End If
End If
Else
Get #FileHandle, , iValue: lRead = lRead + 2&
If iValue = ResID Then
bOk = True
Exit Do
End If
End If
End If
End If
lRead = lHdrSize - lRead + (((lDataSize * 8&) + &H1F&) And Not &H1F&) \ &H8&
If lRead > 0& Then Seek FileHandle, Seek(FileHandle) + lRead
Loop
If bOk Then
lRead = lHdrSize - lRead
If lRead > 0& Then Seek FileHandle, Seek(FileHandle) + lRead
ReDim b(0 To lDataSize - 1&)
Get #FileHandle, , b()
If ResType = RT_ICON Then
pvParseRESfile = CreateIconFromResourceEx(VarPtr(b(0)), lDataSize, True, ICRESVER, 0, 0, 0)
ElseIf ResType = RT_GROUP_ICON Then
For lRead = 7 To lDataSize - 1 Step 14
If b(lRead + 1) = b(lRead) * 2 Then Debug.Print "likely malformated icon directory for Icon: "; ResID
Next
pvParseRESfile = LookupIconIdFromDirectoryEx(VarPtr(b(0)), True, Cx, Cy, 0&)
End If
End If
End Function
Disclaimer: This code should select the same icon that LoadImage selects based on desired width/height.
However, many icon headers are prepared incorrectly. The IconDirectory's 1-byte width & height are the physical size of the icon. Many applications will make the height value = height * 2. And that is incorrect per my interpretation. The BitmapInfo structure must report icon's height as double, but not the IconDirectory structure. When I loaded an icon group with multiple icons where the IconDirectory has the height as doubled, LookupIconIdFromDirectoryEx failed to pick the correct icon. When I fixed the height values, it picked it correctly. You will find documentation all over the web that says the IconDirectory height value is double actual height; however, I haven't found one MSDN article that specifies that. After reviewing Microsoft DLLs, they do not double the height. I'll let Microsoft set the example. RT_GROUPCUSOR are different.
So, what does this mean for people using your code and gripe about the wrong icon size being loaded for the TaskDialog? Education. Point them to this post. Alternatively, if you choose to use the code I posted, you can test for possible malformated IconDirectories quickly and Debug.Print or show MsgBox during IDE. If this sounds like a plan, suggest testing for that with something like the following:
Code:
...
ElseIf ResType = RT_GROUP_ICON Then
For lRead = 7 To lDataSize - 1 Step 14
If b(lRead + 1) = b(lRead) * 2 Then Debug.Print "likely malformated icon directory for Icon: "; ResID
Next
pvParseRESfile = LookupIconIdFromDirectoryEx(VarPtr(b(0)), True, Cx, Cy, 0&)
End If
...
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
That seems to be pretty much the same thing my sample project has... it has the ResIconToHICON function that loads a full, multiple entry icon added as a custom resource (much easier than having to compile the resource manually, just so it's in the icon group instead), and chooses the size closest to the one requested in the call; which is 32x32/16x16 for header/footer in the project.
Does your code just handle external .RES files, or could it do the same manual selection from a DLL with an icon resource group too?
Code:
Public Function ResIconToHICON(id As String, Optional cx As Long = 24, Optional cy As Long = 24) As Long
'returns an hIcon from an icon in the resource file
'Icons must be added as a custom resource
Dim tIconHeader As IconHeader
Dim tIconEntry() As IconEntry
Dim MaxBitCount As Long
Dim MaxSize As Long
Dim Aproximate As Long
Dim IconID As Long
Dim hIcon As Long
Dim i As Long
Dim bytIcoData() As Byte
On Error GoTo e0
bytIcoData = LoadResData(id, "CUSTOM")
Call CopyMemory(tIconHeader, bytIcoData(0), Len(tIconHeader))
If tIconHeader.ihCount >= 1 Then
ReDim tIconEntry(tIconHeader.ihCount - 1)
Call CopyMemory(tIconEntry(0), bytIcoData(Len(tIconHeader)), Len(tIconEntry(0)) * tIconHeader.ihCount)
IconID = -1
For i = 0 To tIconHeader.ihCount - 1
If tIconEntry(i).ieBitCount > MaxBitCount Then MaxBitCount = tIconEntry(i).ieBitCount
Next
For i = 0 To tIconHeader.ihCount - 1
If MaxBitCount = tIconEntry(i).ieBitCount Then
MaxSize = CLng(tIconEntry(i).ieWidth) + CLng(tIconEntry(i).ieHeight)
If MaxSize > Aproximate And MaxSize <= (cx + cy) Then
Aproximate = MaxSize
IconID = i
End If
End If
Next
If IconID = -1 Then Exit Function
With tIconEntry(IconID)
hIcon = CreateIconFromResourceEx(bytIcoData(.ieImageOffset), .ieBytesInRes, 1, &H30000, cx, cy, &H0)
If hIcon <> 0 Then
ResIconTohIcon = hIcon
End If
End With
End If
'Debug.Print "Res hIcon=" & hIcon
On Error GoTo 0
Exit Function
e0:
Debug.Print "modIcon.ResIconTohIcon.Error->" & Err.Description & " (" & Err.Number & ")"
End Function
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
I do not plan on supporting external DLL icon selection, for the following reasons:
1) no guarantee DLL exists on all computers, in all O/S the code will be run on
2) believe option might be used by like 1% of 'educated' users. The other users might fall into above trap
3) If desire exists to use external icons, then code readily exists to do that and hIcon can be passed to the class instead
4) This is the big one: people that want icons for their own programs, include icons in their resource files to ensure portability
Regarding custom resource entries... I am not suggesting that users create custom resources, just for the IDE WYSIWYG workaround
1) When placed in the CUSTOM section, TaskDialog and other APIs won't be able to find them since they will not exist in the RT_GROUPICON section
2) Users cannot use code like: Set Me.Picture = LoadResPicture(...). APIs like LoadImage will not work for those icons
3) I do allow users to pass a CUSTOM resource, but do not require icons be placed there.
Unless using rc.exe or a res file hack, understand that 32bit & Vista-PNG icons will probably reside there
My idea for parsing the uncompiled RES file follows this train of thought
1) Inform users of the code how to get WYSIWYG during IDE, uncompiled
2) In IDE only (class tests for running uncompiled). Use the RES path/file if user provided it
3) The call to pass the path/file can be encoded with #IF #ENDIF if desired
P.S. CUSTOM is default, but users can rename it and create as many as they'd like.
For example, to place AVI files in a resource appropriately named AVI, these easy steps are used
1) Add AVI file to the CUSTOM resource
2) Double click on the newly added AVI item
3) Change the "Type" textbox value to read: AVI
4) Save changes
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
The way I've set mine up supports either way; custom group icons can be used with an hIcon, but regular icon groups are supported too; you would just set .hInst = App.hInstance and then set the icon to the id.
Custom resource icons are really the way to go IMO, the only exception being if you don't want to use icons not supported by the VB resource editor. I use PNG's from there too, with a RenderPNG function that can draw it onto an hdc. The only downside is needing gdiplus.. but still seems significantly less complicated than loading from a .rc file. I'm still going to add in support as you outlined... I'm helpless to project bloat; everything these days is being dumbed down and stripped of features. I believe in having as many features as imaginable. You're talking to the guy who 8 years ago started a simple file renaming program, that's now a 50,000 line behemoth with its own mini-language and regular scripting support, parsing beyond anything ever seen, TVDB/TMDB support, and every file-related extra you can imagine, even full support for libraries using IShellLibrary/IShellItem that comes from the dozen or so modern interfaces I added onto olelib.tlb, since I already used IShellFolder, IEnumIDList, IContentMenu, etc. Is it close to done? Hell no, I think of new things to add constantly.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
On Vista+ you don't need GDI+ for PNG, especially if the PNG is in an icon. CreateIconFromResourceEx will load a PNG-icon as hIcon. In fact, you can use DrawIconEx to render the PNG too. And if one really wanted to get creative, you can load/render any PNG of 256x256 or less with those APIs if one were to create a icon structure for CreateIconFromResourceEx to use (I'm making an assumption that the API doesn't restrict png-icon to 256x256 only). I'd suspect that LoadImage would also, but on XP and lower, GDI+ or another option is needed. And I'd like to stress this one more time, I don't feel custom resource is the way to go. VB, APIs and Windows itself will go to the app's resource for loading images. Nothing in the Custom resource section is usable to them
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
fafalone
That seems to be pretty much the same thing my sample project has... it has the ResIconToHICON function that...
If you wish to use the API to determine best match (should guarantee same results as window's APIs), you can use/abuse the following code.
Basically, it takes LoadResData() results, converts the IconDirectory to dll format & uses API to choose best match
When I first posted this, no UDTs were used & was kinda hard to read unless one was really fluent with the icon file/dll structures.
I revisited it, using your UDT structures so it could be followed easier. Hopefully no typos -- air code; I don't use those structures
Code:
' icoData() is the icon bytes returned from LoadResData() or read from ico file
' desiredSizeX is width: 16, 24, 32, etc
' desiredSizeY as height
Dim lPtrSrc As Long, lPtrDst As Long, lID As Long
Dim icDir() As Byte, LB As Long
Dim tIconHeader As IconHeader
Dim tIconEntry As IconEntry
LB = LBound(icoData) ' just in case a non-zero LBound array passed
' convert 16 byte IconDir to 14 byte IconDir
CopyMemory tIconHeader, icoData(LB), Len(tIconHeader)
ReDim icDir(0 To tIconHeader.ihCount * Len(tIconEntry) + Len(tIconHeader) - 1&)
CopyMemory icDir(0), tIconHeader, Len(tIconHeader)
lPtrDst = Len(tIconHeader)
lPtrSrc = LB + lPtrDst
For lID = 1& To tIconHeader.ihCount
CopyMemory tIconEntry, icoData(lPtrSrc), 12& ' size of standard tIconEntry less last 4 bytes
tIconEntry.ieImageOffset = lID
CopyMemory icDir(lPtrDst), tIconEntry, 14& ' size of DLL tIconEntry
lPtrDst = lPtrDst + 14&: lPtrSrc = lPtrSrc + Len(tIconEntry)
Next
lID = LookupIconIdFromDirectoryEx(VarPtr(icDir(0)), True, desiredSizeX, desiredSizeY, 0&)
Erase icDir()
If lID > 0& Then
CopyMemory tIconEntry, icoData(LB + (lID - 1&) * Len(tIconEntry) + Len(tIconHeader)), Len(tIconEntry)
hIcon = CreateIconFromResource(VarPtr(icoData(LB + tIconEntry.ieImageOffset)), tIconEntry.ieBytesInRes, True, ICRESVER)
End If
Here are my API declarations. Pay attention to how I used ByRef & ByVal
Code:
Private Declare Function CreateIconFromResource Lib "user32.dll" (ByVal presbits As Long, ByVal dwResSize As Long, ByVal fIcon As Long, ByVal dwVer As Long) As Long
Private Declare Function LookupIconIdFromDirectoryEx Lib "user32.dll" (ByVal presbits As Long, ByVal fIcon As Long, ByVal cxDesired As Long, ByVal cyDesired As Long, ByVal Flags As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
How is ICRESVER set? Per MSDN I set it to &H30000 and the function seems to work for both LoadResData and an external .ico, but I'm wondering under what scenarios it would be different and how to determine that.
(also typo: CopyMemory icoDir instead of icDir)
PS- Since you're really familiar with icons and we're on the topic; any quick way to lookup actual ID from the sequential index when loading icons from a DLL file? I made an icon browser and quickly realized they don't match, and was going to try to avoid the rather complex way to load them 'properly' by reading the resources manually.
-
Re: [VB6] TaskDialogIndirect: Complete class implementation of Vista+ Task Dialogs
Yes ICRESVER is &H30000. As for difference between your current manual icon selection logic and how Windows does it, refer to this link & jump down to the section titled "Choosing an Icon"
Icons in a DLL really don't have a sequential index, per se. Icons in a DLL are grouped by RT_GROUP_ICON. These groups have names that are either integer values or string values. It is up to the resource compiler how they are named. For example, adding an icon to VB res file, VB typically names 1st one 101. That is the Group name. You can change it to anything you want. Within each Group, there will be 1 to several individual icons. These always have a numerical/integer ID and the Group's IconDirectory has, as the last member of each IconDirEntry, the ID.
So, within a DLL, unless one wants to manually parse it, ugh!, you would use EnumResourceNames API, looking for RT_GROUP_ICON entries. Within the callback procedure, you'd keep count of how many Groups there . Now, you could consider those sequential indexes. 1st Group found is 1, 2nd is 2, etc. During this enumeration, you could keep a reference of actual Group Name per index.
Then if wanting a specific "index" and you have the actual RT_GROUP_ICON name:
1) Use LoadImage API to create icon
2) Use LoadResource (plus other APIs) to get ICONDIR, then LookupIconIdFromDirectoryEx for iconID, then LoadResource for iconID, & finally: CreateIconFromResourceEx
If you don't have the actual RT_GROUP_ICON name, then:
1) Call EnumResourceNames and on the nth iteration (equivalent to "index") within the callback routine, stop enumeration & cache the actual Group name
2) With the Group name, previous option above applies
The individual icons are sequentially indexed in the DLL, whether some indexes may be skipped or not, I don't know (compiler decisions). But their index really has no direct relationship to the Group they belong in. Usually the 1st individual icon is indexed #1, 2nd is #2, etc. There is no guaranteed that #1 and #2 are from the same Group or if they are in the same Group, they are in the same order as in the Groups IconDirectory (usually they are).
So, using an entry in VB res file for example: User added 2 icons, each contains 3 images: 16x16 32x32 48x48 and named the entries 101, MYICON repsectively
1st RT_GROUP_ICON name: 101
RT_GROUP_ICON directory will have 3 indexes: 1, 2, 3
RT_ICON with name of 1 will be 1st image in that group, 16x16
RT_ICON with name of 2 will be 2nd image in that group, 32x32
RT_ICON with name of 3 will be 3rd image in that group, 48x84
2nd RT_GROUP_ICON name: MYICON
RT_GROUP_ICON directory will have 3 indexes: 4,5,6
RT_ICON with name of 4 will be 1st image in that group, 16x16
RT_ICON with name of 5 will be 2nd image in that group, 32x32
RT_ICON with name of 6 will be 3rd image in that group, 48x84
*NOTE: The order of the RT_GROUP_ICONs above is a guess. Whether the compiler writes 101 before MYICON or vice versa is unknown & may depend on compiler criteria/choice
When that project is compiled, EnumResourceNames will return 2 RT_GROUP_ICON entries: 101 & MYICON. And EnumResourceNames will return 6 RT_ICON entries: 1-6
Unless you absolutely need to enumerate the DLL, it should be far simpler to use LoadImage if you know the RT_GROUP_ICON name. You'll most likely want to use LoadLibraryEx first (with the LOAD_LIBRARY_AS_DATAFILE constant), to retrieve the hMod parameter for LoadImage. LoadImage allows loading by Group ordinal (Integer name value) or by Group name as string (either text like "MYICON" or ordinal preceded by pound: "#101")
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Ah yes that's the really complicated way :)
I was using
Code:
Dim hr As Long, lCount As Long
lCount = ExtractIconEx(sCurIconFile, -1, 0, 0, 0)
ReDim glLargeIcons(lCount)
ReDim glSmallIcons(lCount)
For l = 0 To lCount - 1
hr = ExtractIconEx(sCurIconFile, l, glLargeIcons(l), glSmallIcons(l), 1)
Call ImageList_AddIcon(himl, glLargeIcons(l))
Call ImageList_AddIcon(hIMLs, glSmallIcons(l))
Call DestroyIcon(glLargeIcons(l))
Call DestroyIcon(glSmallIcons(l))
Next l
Extracts all the icons and shows the 32x32/16x16 icons perfectly; but it goes sequentially.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Yes, ExtractIconEx would be easier and at a small cost of not being able to dictate sizes other than small/large.
Since it is doing this sequentially & I validated that, I wouldn't be surprised if, internally, it doesn't do exactly how I'd do it:
1) EnumResourceNames using RT_GROUP_ICON
2) For each group: call LoadImage with the RT_GROUP_ICON name
Not really a lot of work, other than setting up the callback function. My alphaimage control uses a similar method
Oh, one drawback with using this method with the TaskDialog.
I know you are aware that when setting the header/footer icons, you can opt for preset icons (i.e., exclamation, shield, information, etc). But not sure you are also aware that you can use the RT_GROUP_ICON name of any icon within the imageres.dll; only stipulation is that dialog config's .hInstance is zero & of course dwFlags doesn't say to use hIcon. By using the actual group name, TaskDialog will extract the icon for you. If you choose to replicate that functionality manually, guess just a matter of customer education that they know to provide the sequential index of the imagres icon vs. the icon's name/ordinal
Edited: Actually that API can load icons by their ordinal within the dll. Want icons from RT_GROUP_ICON #5? Pass -5 as the icon to extract. Documentation doesn't indicate the API can extract string named groups; not that they are that common, but do exist. Vista's imageres.dll even has one.
Last but no least. If you google update or customize imageres.dll, you will find lots of hits regarding customizing that dll. If trying to provide icons from the dll by sequential indexing, no guarantee that the icon will be in the same spot on everyone's pc. Guess it is possible though that the original imagres Group names could be modified by someone recompiling that dll. I do plan on educating users that they can use that dll for selecting icons; however, it will come with 2 strong caveats:
1) As mentioned above -- no guarantee the icon they selected on their system will exist on some target system due to customization of the dll
2) Strongly suggest using icons from the Vista imageres.dll as newer O/S will likely not remove any, but append lots of new ones
I'm not at that point yet, but did want to do a comparison between those dlls on Vista & Win7 and see if my assumption is correct. If not correct, may not even offer that option
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Just FYI. I was playing with the idea of allowing icons to be added to buttons.
TaskDialog allows a single icon, the UAC elevation icon. But thought it could be a nice touch to allow option for icon-only, text+icon, or text-only button captions.
Downside: On Vista at least, cannot reliably cross-reference a TaskDialog button ID with the actual button window handle in order to use SendMessage bm_setimage as we need the button's hWnd. The dialog does not assign button/control IDs to the actual buttons it creates, they all have the same value as zero (i.e., GetWindowLong(GWL_ID)=0 ). There are ways to synchronize these, but are not 100% reliable. Obviously, dialog keeps an internal cross-reference between the Button ID assigned via the config structure & the button's actual window handle
1) EnumChildWindows of the dialog, looking for all "Button" class windows that are not option buttons. Get their text & compare that to the captions assigned during setup
-- Option buttons are "Button" class windows also, but their style can be validated via GetWindowLong() to distinguish them from push-button & command-link
-- logic flaws: 1) what if someone changes button text outside of your control 2) for comparison, how to know the captions for common buttons that may be using different languages
2) EnumChildWindows of the dialog, using ZOrder. Custom buttons displayed in order of first added, first displayed. Common buttons follow in a specific order
-- in other words, if 4 physical non-optionbuttons exist, then 0 or more are custom buttons, while the remaining, if any, are common buttons
-- logic flaws: 1) what if someone added/removed button outside of your control or tweaked the ZOrder? 2) What if later versions of dialog add more common buttons or changes display order?
Maybe Win7 or later assigns button IDs to the actual button windows and/or includes a custom message to be sent for assigning custom graphics. Until then...
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Hmm.. maybe a Vista-only limitation?
On Win7 I took a look and Spy++ shows it's a Button class, gets the caption, and shows a unique non-zero handle for each button. I had briefly looked into this where others had asked, and the answer was a resounding 'no' and to use a very nice emulated version. Additionally it shows the same class and unique handles for CommandLinks... so maybe both are possible (!).
I'd definitely like to have that feature, let me see if I can get it to respond to BM_SETIMAGE.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Well, heck yes then. Make it a Vista-only limitation in code & easy enough to check when GetWindowLong(GWL_ID)=0
Now the assumption is that the ControlID value can be related back to the assigned Button ID. Would think the common button control IDs would be equivalent to the windows constant, i.e., Ok = vbOk = IDOK
Glad I didn't destroy all my code relating to button captions :)
Edited: Maybe joyful too soon? Unique handle is expected as every window handle must be unique. What we should be looking for in Spy++ is the ControlID value, not the hWnd
-
1 Attachment(s)
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
fafalone, did it & believe it is truly reliable regarding building cross-reference between dialog buttons' hWnds and the configuration structure's button IDs.
You may not want to wait around until I post my project, so I'll give you the outline & you can choose whether or not you want to pursue it also
The idea is kinda like a teacher with a student list but not know who the students are. Call out their name & wait for the answer back. The button's ID is the name & the subclassed WM_ENABLE message results in the answer back.
1) During TDN_CONSTRUCTED/NAVIGATED, I call EnumChildWindows to return each window on the dialog
-- For each child window on the dialog's hWnd, I filter out all but just Buttons, but also exclude option buttons (not supporting images on those)
-- For each button (normal or link), I cache it's original window procedure to the button's hWnd with SetProp, then subclass the window to my procedure
2) When EnumChildWindows returns, all buttons I care about are subclassed. At this point, dialog isn't shown yet
3) I loop thru each custom & common button and send a TDM_ENABLE_BUTTON message to disable the button (otherwise if enabled & enabling it, it is ignored)
-- when the button receives a WM_ENABLED message in my procedure, I update a class-level variable with the button's hWnd & within that procedure unsubclass the button
-- when the SendMessage call returns, if the hWnd value is not zero, then
:: I store that hWnd to my cross-reference list for the button ID I just disabled & reset that variable for next button
:: I also call RemoveProp to undo what I did in step #1 above
-- at this point, I re-enable the button (unless tagged as disabled during setup) and set the icon (if one tagged during setup)
-- note that preset/common button IDs are set by system, but are system constants: Ok = IDOk, Yes = IDYes, etc
4) When the loop is complete, I call EnumChildWindows one more time. It is looking for the same buttons as the first time it was called
-- this time instead of subclassing buttons, it is calling GetProp to see if the property set in step #1 is still on the button
:: If property still exists, then didn't unsubclass, so do that now & remove the property
5) Clean up complete. Button ID-to-hWnd reference complete. Now I can use any BM_xxx messages on the buttons
P.S. After icon is set, disabling the button later on, does indeed render the icon disabled too. Cool
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Going to cogitate on a less intense way of matching the hWnds, but yeah I'm definitely going for it... might or might not limit support to custom buttons depending on how things go.
Also something neat to consider, if you're using the CommandLink style buttons, it will allow different sized icons and show them at their correct size. So you can pick the 32x32 size and it shows at that size on CL's, or you can use 16x16 and it shows at that size too. Larger icons will even show... but the bottoms get clipped off if you have multiple buttons-- you need to add lines. So I'm going to have to add a check to see if the given button is a commandlink, and select the bigger size- probably add a flag like TDF_CLICONSIZE_32 / 48 to allow the user to choose whether they want the bigger icon displayed. Can't just always pass a big icon; it will not scale down to 16x16 if it's on a normal button.
http://i.imgur.com/bHuit0x.jpg
As you can see, it's not upscaled, it's using the full 48x48 icon.
Edit: So the method I'm trying out now; I'm going to operate under the scenario that the enumeration order matches the constructed order, which matches the button array order (common buttons are done in a particular order too). Will investigate scenarios where this might be false.. but for now I'm not keen on setting up a whole subclassing apparatus. It remains true with TDF_RTL_LAYOUT.
Edit2: Going good so far. Custom buttons are up and running. Radio buttons are always drawn first, so I added an offset if they exist.
http://i.imgur.com/q3Z1WzP.jpg
Edit3: Took an hour, but got the logic of supporting the common buttons worked out. Was headache-inducing and would have probably been easier to subclass. But it's all working now. Even adding in an option to load it from shell32 or imageres, which is pretty simple since I'm already using them as a main/footer icon option. Simply added a flag to indicate the hIcon should be interpreted as a LoadImage call. Also added a SetWindowsButtonIconSize sub for bigger commandlink icons with this too.
http://www.vbforums.com/images/ieimages/2014/09/1.jpg
Process used:
AddButton accepts hIcon, stored in separate array of same dimensions of total buttons
Enum on created/navigated populates m_hButton(), handily enum api has the lParam to pass an objptr to make this callback work identical to the main callback
SetButtonIcons loops through icon array setting non-zeros to the corresponding index in hwnds.
Behavior note: BM_SETIMAGE works on CommandLinks even if TDF_USE_COMMANDLINKS_NO_ICON is specified.
PS- How did you get the colored background without a shield icon?
PS2 - You mentioned 'BM_xxx'... sounds like you're thinking of going further... why not have dropdown buttons?
http://i.imgur.com/04XHqQS.jpg
Oh boy.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
I originally thought going the same route: custom buttons displayed before common buttons & assume order is what is expected. Opted out simply because I didn't want the code to foul up if a different version of the API changed things about. The subclassing approach should be 100% since I am using the API to fire off a message by button id & getting the hWnd from the button that responds to it.
Note: normal buttons will take any size icon also, without scaling, but obviously anything > 16x16 won't display correctly on a normal size button. My code offers user-defined sizes for button icons also, as was already aware that they are not scaled by the dialog.
Regarding other BM messages: was thinking about split buttons, but not sure I am going that route... I do use SetWindowText to offer button caption changes at runtime
The colored header? Niffty but limited. A few undocumented preset icons (I don't have the code in front of me, but believe they are -6, -7, -8, -9 (may be off by 1). The colors are blue, red, green, yellow, gray. When one of those icons assigned, header only, header gets filled in & applies the associated icon. To replace the icon simply set the header icon to another icon or zero to remove it. But see TDM_UPDATE_ICON documentation for restrictions
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Oh ok. Thought there something I was missing to get it like that with normal setups.
----
Project officially updated with icon button support, and native executing of links if you want the default behavior (event is still fired for customization).
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Forgot to ask..have you looked at the enumwindows and bm_setimage on vista? Are the button hwnds not returned by enumchildwindows?
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
hWnds are always returned, regardless of the system. The only way a 'control' hWnd won't be returned is if the control is not an actual window but is owner-drawn on the DC, kinda like VB's Image & Label controls.
What would've made any workarounds completely unnecessary is if the dialog would've put the button IDs in the control's ControlID property, then we could've just used GetWindowLong(GWL_ID) within EnumChildWindows to return the hWnd for each button ID.
The screenshot I posted with the icon-only sample was on Vista.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Curious, have you found a clean way of displaying the command links without icons & without the green arrows?
My workaround works, but feel it should be easier. Again, maybe just a Vista thing? But the flag TDF_USE_COMMAND_LINKS_NO_ICON doesn't work.
I created a 1x1 4bit 100% transparent icon (B&W, 1 bit, didn't work for whatever reason). Code was modified a bit further to allow user-defined width so that if say a 32x32 icon was used on 1 or more command links, then a 32bit wide transparent icon could be used for command links with "no icons" so that all the link captions have the same left margin
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
The flag works fine on my system (Win7 x64).. and MSDN makes no mention of it being new... so I'm not sure what the issue is for you.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Never got it to work from day 1. I'll have to try on Win7 another time. Probably an early bug & not fixed until Win7? I've read other posts on the net of ppl having similar issues
Edited: Got it to work & found the problem. When both that flag and the TDF_USE_COMMAND_LINKS were added, the latter overrode the former. Now that I see how these links captions don't line up when one has an icon and one doesn't, gotta admit, I like my 'transparent icon" solution to align them up
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
So the magic of the project, why not update it? This is the final version of it?
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
All of the native features of task dialogs have been implemented, so anything else would be another customization.
I'm definitely open to suggestions though if there's something you'd like to see the class be able to do natively. Or if you can suggest an easier way to implement some of the more complex features, I know some of it is a little tough. And of course I'll fix any bugs that are identified.
Let me know :thumb:
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Hi fafalone,
tested this in one windows 10 workstation. Generates an error
453 Can't find DLL entry point TaskDialogIndirect in comctl32.dll.
Question - any idea what might cause of this (possibly manifest error)?
Edit... Yes, that was the reason for the error.. Forgot to add manifest to project and then compiled exe didn't use comctl 6 version and was unable to find entry point.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Yeah even direct API calls need a manifest the way they set it up for backwards compatibility.
---------------------------------------------
Unrelated, but in the main post is everyone else seeing tiny images (besides the header image)? When I first made it they appeared full sized, and the images themselves haven't changed.. but now they're tiny on both Firefox and Chrome. Is this a Firefox change or a VBForums change, and could it be fixed without giving up the table layout?
Edit: Nevermind, figured out I could fix it by using td="width: 75%"
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Quote:
Originally Posted by
LaVolpe
[...]
The colored header? Niffty but limited. A few undocumented preset icons (I don't have the code in front of me, but believe they are -6, -7, -8, -9 (may be off by 1). The colors are blue, red, green, yellow, gray. When one of those icons assigned, header only, header gets filled in & applies the associated icon. To replace the icon simply set the header icon to another icon or zero to remove it. But see
TDM_UPDATE_ICON documentation for restrictions
Revisited this today; have you actually tried replacing it with another icon? It doesn't actually seem to work. Passing 0 to get no icon works fine, but passing another icon index doesn't; I tried a bunch of different values, making sure to pass through MAKEINTRESOURCE, and even tried without doing that. Just curious if it worked for you and I'm missing something?
Edit: Nevermind, was missing that I had been defaulting the hInstance to App.hInstance instead of 0.
That leaves the big question; how to replace it with a custom icon via an hIcon? You can definitely get a custom icon in there by using App.hInstance since the gradient is part of the system resource it loads.. but given the pain involved in getting nice icons into VB resource files (as icons, specifically the icon group used here), maybe its easier.
Shield icon from gradient replaced with custom icon stored in application resource file:
http://i.imgur.com/1HViAfD.jpg?1
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Hi, how i can implement your td.res resource file in my existing project becuase i already have *.res file on my project and vb not allowed to add more than one *.res file.
Thanks
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
If you're using La Volpe's manifest creator you can select an existing resource file instead of creating a new one; that will insert the manifest into your resource file, or you can do that manually with another resource editor.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Ok, so I decided that no, I have not gone far enough with rarely used features. I want to move forward with making dropdown menu buttons work.
Ran into a problem though;
I'm not getting BCN_DROPDOWN in the WndProc after subclassing the taskdialog's hwnd to it.
If use SetParent to make sure that's the immediate parent, not only do I still not get BCN_DROPDOWN, but it breaks the button (left click doesn't close the dialog, and even if i did that manually it would be a bigger pain because i'd have to manage the return codes)
Heck I'm not even getting BCN_DROPDOWN when I subclass the button hwnd itsself.
Edit: Muahaha I got it. For some reason, while my normal split buttons fire a WM_NOTIFY with BCN_DROPDOWN, with the task dialog I don't get that but do get BCM_SETDROWNDOWNSTATE, which is all I need:
http://i.imgur.com/OdyCSJN.jpg
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Hi Fafalone, like many others, I am using this constantly now. However I really could use a Textbox to input a string as well. The Inputbox is so old fashioned and I have seen a textbox on a friends CodeJock version of the TaskDialog. Any chance?
Steve.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
There's no native option to have a textbox, so it's probably a custom control. Bolting a textbox onto the Windows task dialogs is certainly possible, but in a stable, reliable manner? Tricky, to say the least. But the even bigger problem would be positioning. Figuring out where the box could be placed given the vast array of options and changing number of lines based on text length?
Really far better off just making a regular form, although I'll look into maybe doing something like replacing the content area with one? or better ideas?
Edit:
I was able to work out adding one in a stable manner; it was a little tricky because it was locked and ugly when I tried to create it as a child of the main taskdialog window, but it worked perfectly when I tied it to the DirectUIHWND class (the class already has an EnumChildWindows call for other features),
http://i.imgur.com/u2L0ONg.jpg
Also set it up to return like the other results; i.e. .ResultMain .ResultVerify .ResultRadio and now .ResultInput too
Pressing Enter even works, returning with the current/default button.
So now the biggest challenge left is figuring out where to position it. How to set it to be in what location and present this as a user-friendly option.
Suggestions?
Code:
hEditBox = CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "", _
WS_VISIBLE Or WS_CHILD Or WS_BORDER Or ES_LEFT, _
20, 100, 150, 22, _
hUI, IDD_EDIT1, App.hInstance, ByVal 0&)
Dim hFont As Long
hFont = GetStockObject(DEFAULT_GUI_FONT)
If hFont Then
SendMessageW hEditBox, WM_SETFONT, hFont, ByVal 0&
End If
Edit 2:
I've got some good ideas going now, check this out:
Default placement, without a main instruction:
http://i.imgur.com/9n4Nl22.jpg
and with:
http://i.imgur.com/esAdB8k.jpg
Only requiring the addition of a custom flag, .Flags = TDF_INPUT_BOX
Then check this bad boy out:
http://i.imgur.com/PEq2mwQ.jpg
Made with
Code:
With TaskDialogPW
.Init
.MainInstruction = "Authorization Required"
.Content = "The password is: password"
.Flags = TDF_INPUT_BOX
.InputIsPassword = True
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.SetButtonElevated TD_OK, 1
.SetButtonHold TD_OK
.Footer = "Enter your password then press OK to continue."
.IconFooter = TD_INFORMATION_ICON
.IconMain = TD_SHIELD_ERROR_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Private Sub TaskDialogPW_ButtonClick(ByVal ButtonID As Long)
If ButtonID = TD_OK Then
If TaskDialogPW.InputText = "password" Then
TaskDialogPW.CloseDialog
'and do whatever
Else
TaskDialogPW.Footer = "Wrong password, please try again."
TaskDialogPW.IconFooter = TD_ERROR_ICON
End If
End If
End Sub
I still don't really know what to do about figuring out placement elsewhere, which can be really important if there's no footer area or common buttons to align it with (how it's being done now)
And even with this placement; it won't work with the Verify checkbox or the dropdown for more info control..
Ok so I think I've got it worked out... there's 3 alignment options.. the one above, aligned to the buttons
this one, aligned to the footer icon (or just plain bottom if there is no footer, but it's meant to be used with the icon)
http://i.imgur.com/3mjF1l7.jpg
and what I'm planning on making the default, aligned to be just above the common buttons and width-aligned with the dialog, content-align:
http://i.imgur.com/QzueXws.jpg
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
I went round to see my mate just now to get him to show the Dialog with a Textbox for input and get it fresh in my mind. His textbox stays in the white area just below the Content Text. This leaves the space to the left of the buttons for the Checkbox. I tried putting a long string of text in the content and the textbox just moved down (always in the white though) as it should.
They say a picture is worth a thousand words so;
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Yup that's precisely how the default alignment option I've set works (I think I put in that edit with the 3-alignment-option pictures after your post, so re-read above too). It ensures there's always one line break to make sure the edit box isn't on top of the text; alignment like that would just be two. My textbox position is based on the buttons, so it doesn't matter how many lines the content text takes up; it will just always be there in the white space between the end of content and the gray area.
Here's how it would look as of right now:
http://i.imgur.com/4H4JuJl.jpg
The only real difference is the size of the gap (besides the theme elements; I'm on Win7, the buttons, check, and title will look like yours on Win8+)... but I can't adjust it any further upwards without a major complication to detect a double-crlf and adjust a whole bunch of alignment stuff accordingly.
So hopefully it's ok with that spacing.
Edit - ok ok I made the adjustment, now if it ends with a double-break it will align like this
http://i.imgur.com/mnhcDIC.jpg
Simple to call:
Code:
With TaskDialog1
.Init
.MainInstruction = "Duplicates"
.Content = "If you want to exclude an Artists name from the search:" & vbCrLf & vbCrLf
.Flags = TDF_INPUT_BOX Or TDF_VERIFICATION_FLAG_CHECKED
.AddCustomButton 100, "Continue"
.CommonButtons = TDCBF_CANCEL_BUTTON
.IconMain = TD_SHIELD_ICON
.Title = "cTaskDialog Project"
.InputText = "Enter Artist name here." 'Will be selected by default
.VerifyText = "Exclude Jingles"
.ParenthWnd = Me.hWnd
.ShowDialog
End With
Should be able to post the update later today, just gotta clean things up, now that I'm done with a massive fall down the rabbit hole where I made the position automatically adjust to an expanded info click-- much much harder than it seemed going in, but now working.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
cTaskDialog 0.7 Released!
Two new features:
The first is just a minor option, a custom flag, TDF_KILL_SHIELD_ICON to remove the shield icon but keep the gradient background.
But the big one: INPUT BOXES!
Now you can take advantage of a wide range of TaskDialog options with a TextBox to retrieve user input, with the new custom flag TDF_INPUT_BOX
First, a basic input box with the default positioning:
Code:
With TaskDialog1
.Init
.MainInstruction = "Hello World"
.Content = "Input Required"
.Flags = TDF_INPUT_BOX
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Label5.Caption = .ResultInput
If .ResultMain = TD_OK Then
Label1.Caption = "Yes Yes Yes!"
Else
Label1.Caption = "Cancelled."
End If
End With
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
We can also position it next to the buttons:
Code:
With TaskDialog1
.Init
.MainInstruction = "Input Required"
.Content = "Tell me what I want to know!" & vbCrLf '& vbCrLf
.Flags = TDF_INPUT_BOX
.InputAlign = TDIBA_Buttons
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
The last placement option is to use it as footer input. It's designed to use the normal footer icon, but have a textbox instead of a label:
Code:
With TaskDialog1
.Init
.Content = "Something somesuch hows-it what-eva" '& vbCrLf
.Flags = TDF_INPUT_BOX 'Or TDF_USE_SHELL32_ICONID
.InputAlign = TDIBA_Footer
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.IconFooter = TD_INFORMATION_ICON
.VerifyText = "Check here if you want to provide extra info below:"
.Title = "cTaskDialog Project"
.Footer = "$input"
.ParenthWnd = Me.hWnd
.ShowDialog
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
Here's a practical example showing it with default text (which is pre-selected automatically):
Code:
With TaskDialog1
.Init
.MainInstruction = "Duplicates"
.Content = "If you want to exclude an Artists name from the search:" & vbCrLf & vbCrLf
.Flags = TDF_INPUT_BOX Or TDF_VERIFICATION_FLAG_CHECKED 'Or TDF_USE_SHELL32_ICONID
.AddCustomButton 100, "Continue"
.CommonButtons = TDCBF_CANCEL_BUTTON
.IconMain = TD_SHIELD_ICON
.Title = "cTaskDialog Project"
.InputText = "Enter Artist name here."
.VerifyText = "Exclude Jingles"
.ParenthWnd = Me.hWnd
.ShowDialog
Note the spacing; there's always a single line break like the first one (appended if not present), but using a double-break relaxes the spacing a bit.
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
If CommandLinks are being used, the default alignment (content) places the textbox between the content and the first commandlink:
Code:
With TaskDialog1
.Init
.MainInstruction = "You're about to do something stupid."
.Content = "First, tell me why?"
.IconMain = TD_INFORMATION_ICON
.Title = "cTaskDialog Project"
.Flags = TDF_USE_COMMAND_LINKS Or TDF_INPUT_BOX
.AddCustomButton 101, "YeeHaw!" & vbLf & "Put some additional information about the command here."
.AddCustomButton 102, "NEVER!!!"
.AddCustomButton 103, "I dunno?"
.ShowDialog
There's also compatibility with the expanded-info control in the default positioning. The position of the box is automatically adjusted when the expando control is clicked.
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
Finally here's a detailed practical application that shows some more features. The textbox can be set as a password box. We can then combine that with a button hold, so now when enter or ok is pressed, we check the input and only let OK execute if it's a match- while cancel still closes it right away.
Code:
Set TaskDialogPW = New cTaskDialog
With TaskDialogPW
.Init
.MainInstruction = "Authorization Required"
.Content = "The password is: password"
.Flags = TDF_INPUT_BOX
.InputIsPassword = True
.InputAlign = TDIBA_Buttons
.CommonButtons = TDCBF_OK_BUTTON Or TDCBF_CANCEL_BUTTON
.SetButtonElevated TD_OK, 1
.SetButtonHold TD_OK
.Footer = "Enter your password then press OK to continue."
.IconFooter = TD_INFORMATION_ICON
.IconMain = TD_SHIELD_ERROR_ICON
.Title = "cTaskDialog Project"
.ParenthWnd = Me.hWnd
.ShowDialog
Then we need to handle the ok button click to check the password:
Code:
Private Sub TaskDialogPW_ButtonClick(ByVal ButtonID As Long)
If ButtonID = TD_OK Then
If TaskDialogPW.InputText = "password" Then
TaskDialogPW.CloseDialog
Else
TaskDialogPW.Footer = "Wrong password, please try again."
TaskDialogPW.IconFooter = TD_ERROR_ICON
End If
End If
End Sub
|
http://www.vbforums.com/images/ieimages/2014/09/1.jpg
http://www.vbforums.com/images/ieimages/2014/09/1.jpg |
That about covers the main features. One last minor thing, the CueBanner can be set with .InputCueBanner, but isn't visible at first since focus is set on the textbox so the user can start typing right away without clicking first.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
OMG! You are amazing Faf! Talk about excellent service. That is exactly what the doctor ordered, with one tiny caveat. Can I be cheeky and ask for the textbox to have both its current 3D look and also like the picture I submitted which is just flat with a blue (I think) border.
Even if you choose to ignore my cheek :rolleyes:, very well done from the UK.
Steve.
-
Re: [VB6] TaskDialogIndirect - Complete class implementation of Vista+ Task Dialogs
Well THAT level of detail I hope I can at least leave to you... especially since it's just a standard edit control that appears different in different versions of Windows. In the class' AddInputbox sub, the box is created with a CreateWindowEx call. Instead of WS_EX_CLIENTEDGE, try some other styles, 0 for completely flat.. WS_EX_STATICEDGE seems slightly thinner, maybe that's it? You can get all sorts of borders between that and the regular WS_ styles that go with WS_CHILD et al.
What OS are you on, I'd have to look on that.
And it was a great idea to have an input box... can't believe I hadn't thought of that when a few posts ago you saw me worrying about dropdown button menus (saving those for the next version!). As soon as you mentioned that I knew it must happen right away.
Edit:
Actually, it looks like just removing ES_BORDER should do the trick. That looks more like normal textboxes. I'm going to update the class like that...
Old: http://i.imgur.com/nNv1lUt.jpg
New: http://www.vbforums.com/images/ieimages/2014/09/1.jpg Win8: http://i.imgur.com/hMJp0zd.jpg
Yeah the border on Win8 comes out slightly darker... I don't have a Win10 VM set up yet but if normal textboxes look like yours, then so should this.