-
Notifications
You must be signed in to change notification settings - Fork 999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Windows Task Dialog #146
Comments
I like this idea in general. What I'd like to suggest first though is an additional and more detailed concept as a separate issue which depicts the rationale/business value, public surface area (Class, Methods, Properties, etc.), small code samples, maybe along with a link to a repo fork which would include the work that you already did (probably with way fewer partial classes/code files ;-) ). Something I could put the label api-suggestion on. Thoughts? |
@KlausLoeffelmann I might want to take a stab at integrating @kpreisser's library into WinForms core. I'm not too good at writing formal rationales, though. I hope this won't be a problem. 😄 One other question before I begin, however. Where should I put the native types when I start work on this branch? I know WinForms uses three separate classes: |
@KlausLoeffelmann BTW: I suggest to put api-suggestion on any issue which will most likely require API process. api-suggestions are enhancements which require additional process of API review and explicit approval on the API shape. |
Hi @KlausLoeffelmann and @wjk, I would be happy to contribute my implementation of the Task Dialog, but note that there are some areas in the API which I think are not yet ideal from an OOP point of view. Also, I implemented some unofficial features which probably shouldn't be in the public API. I have created the branch The workflow that I used in my Task Dialog implementation is something like this:
The TaskDialog supports both standard/common buttons (like Yes, No, Cancel etc) that are supplied as flags to the native API, and custom buttons where you can set your own string, which are supplied as array in the native API. However, common buttons are currently not represented as objects, as I wasn't sure how to ideally do that. For example, you can use custom buttons like this: // Create and add the buttons.
var button1 = taskDialog.AddCustomButton("My Button 1");
var button2 = taskDialog.AddCustomButton("My Button 2");
// Set properties.
button1.Enabled = false;
button2.ElevationRequired = true;
// Add click event handlers.
button1.ButtonClicked += (sender, e) => { /* ... */ };
button2.ButtonClicked += (sender, e) => { /* ... */ };
// While the dialog is shown: Click the button.
button1.Click(); However, when using standard/common buttons, you have to use the "raw" methods (and you cannot set the // Specify the buttons.
taskDialog.CommonButtons = TaskDialogButtons.Yes | TaskDialogButtons.No;
// Cannot set Enabled/ElevationRequired until the dialog is shown, so
// need to use the Opened event:
taskDialog.Opened += (sender, e) =>
{
taskDialog.SetCommonButtonEnabled(TaskDialogResult.Yes, false);
taskDialog.SetCommonButtonElevationRequired(TaskDialogResult.No, true);
};
// Add click event handlers.
taskDialog.CommonButtonClicked += (sender, e) =>
{
if (e.Button == TaskDialogResult.Yes) { /* ... */ }
else if (e.Button == TaskDialogResult.No) { /* ... */ }
};
// While the dialog is shown: Click the button.
taskDialog.ClickCommonButton(TaskDialogResult.Yes); I think this should be changed to also represent standard/common buttons as objects, but I'm not sure how to do that when keeping the One idea that I had was to create a dictionary-like class that maps a // Specify the buttons.
taskDialog.CommonButtons = TaskDialogButtons.Yes | TaskDialogButtons.No;
// We now can retrieve the button instances.
var buttonYes = taskDialog.CommonButtons[TaskDialogResult.Yes];
var buttonNo = taskDialog.CommonButtons[TaskDialogResult.No];
// or maybe:
//var buttonYes = taskDialog.CommonButtons.Add(TaskDialogResult.Yes);
//var buttonNo = taskDialog.CommonButtons.Add(TaskDialogResult.No);
// Set properties.
buttonYes.Enabled = false;
buttonNo.ElevationRequired = true;
// Add event handler
buttonYes.ButtonClicked += (sender, e) => { /* ... */ };
buttonNo.ButtonClicked += (sender, e) => { /* ... */ };
// While the dialog is shown: Click the button.
buttonYes.Click(); (this would require refactoring so that the Also, the properties and methods related to a progress bar should probably be refactored into a separate What do you think? Thanks! |
Sounds great, modulo the following:
|
It seems like your .NET solution (which is nice) is a fairly literal translation of the C++ API. The C++ API is a bit nasty, though. I think we can make the .NET API nicer and abstract away certain nastiness. For example, why does the dialog need to be opened to be configured? I think the native API likely does it this way because it's the only reasonable way to do it in native code. In object based .NET code we are less constrained. We don't have lifetimes and memory management issues for example. We don't need the Some more concrete points:
|
Hi @wjk, thanks! Yes, the second approach seems better, but I think it might still be good to also support assigning the flags, so that you can assign them in the object initializer (if you just want to add standard buttons but don't need to customize them), just as you can specify button flags in the static Thinking about it, it probably should also be possible to manually create a TaskDialogButton instance (not using the interface, which is also what @GSPP described) and then add it to the collection: var button = new TaskDialogCustomButton("Hello World") {
ElevationRequired = True
}
taskDialog.CustomButtons.Add(button)
var standardButton = new TaskDialogCommonButton(TaskDialogResult.Yes);
taskDialog.CommonButtons.Add(standardButton); I also like the colored status bar icons, but I have to admit I didn't find the values myself (I think I found them on pinvoke.net). Hi @GSPP, You are right, I simply looked at the native Windows API (
Yes, it should be possible to do all the initialization before actually opening the dialog. This is currently the case with custom (and radio) buttons, where you can set properties like
Can you elaborate on these? I know from Unfortunately, the native Task Dialog API only has one implementation where the method does not return until the dialog is closed, even when it is shown non-modal. I also described this behavior in the README (Non-modal dialog). This means you can open multiple non-modal Task Dialogs that the same time, but each dialog will cause a new
This should be possible. Currently, the static When we have classes that represent these buttons (e.g.
Unfortunately, with the native API it is not possible to distinguish a close through clicking on the "Cancel" button from a close caused by clicking the Window X button, as in both cases the result returned by
I initially used these interfaces so that I could make nested classes within Do you mean to use only a single class to represent both a standard/common button, and a custom button?
I also thought about this, as it is not intuitive that you have to call It should be possible to apply the changes immediately when you set the property and the dialog is shown. This has a small disadvantage however, because if you want to navigate the dialog (and therefore change the properties), the displayed Task Dialog would (unnecessarily) update its GUI even though shortly after that it will be navigated (which means it will completely reconstruct the GUI elements). From a performance view, I think this is negligible because navigation is much more expensive than a simple update. The only visible effect would be that if you change the property, then wait some time (meaning the GUI event loop continues) and then navigate the dialog, you would see that the Task Dialog GUI is already changing before the navigation, but I think the user should be able to expect this, and only change the properties directly before navigation. Or maybe the
Navigation basically destroys all currently displayed GUI within the Task Dialog window, and then creates a new GUI from the supplied Navigation is also resource-intensive and much slower than a simple "Update". However, with an Update you can only update following GUI elements/properties:
E.g. you cannot update the text of custom buttons, this is only possible using navigation. My model that I implemented for navigation is like:
This is similar to the native API, where you create a Thank you! And sorry for the long text... 😇 |
Another issue is that the parent window must be specified. Regarding 1, yes this is about modal/non-modal. Both should be possible. For a Regarding 3, I guess I retract this suggestion 😄
Yes, that was my idea but I misunderstood the distinction between the two. They must be two distinct concepts. But custom buttons don't need any inheritance. They always have the same structure: A text string and a click event. Thank you for describing the navigation concept. Maybe it can work like this: The task dialog contents are described by a separate class One more point: Can the dialog be hidden and reopened? That would be nice to make it consistent with any other
This unfortunately means that we cannot abstract this away in the managed API. We could disallow button text changes or automatically "navigate" on text change. I think task dialogs are going to be a very popular feature. Your work is appreciated! Once we have shipped the first version we can never again change the API. We need to get it exactly right. |
Hi @GSPP, thanks for your reply!
Note that the Task Dialog explicitely supports not using a parent window (documentation of the
When not specifying a parent window, the Task Dialog will be displayed as non-modal window (so it does not lock/hide other windows). This is also the behavior with a native MessageBox (if you directly call the
Yes, displaying a non-modal Task Dialog is possible by specifying
Note that in a Task Dialog, there are three different kinds of "buttons":
Common buttons are initially specified by a flags field (enum Both custom buttons and radio buttons are initially specified by a structure containing a custom ID and the display text, and are later identified by that ID. Common buttons, custom buttons and radio buttons have the following members (I'm using the names from my current implementation):
Custom buttons and radio buttons additionally have a Common buttons and custom buttons additionally have the property When clicking a common button or a custom button, the dialog will close by default, but the close can be canceled in the event handler (this does not apply for radio buttons); except for the Therefore, I was thinking to specify a base button class, and then have subclasses for these three kind of buttons.
Although this has a small disadvantage of adding an additional indirection to the TaskDialog when you want to set its properties, I think I like this idea, as it removes the need for the
I think the Task Dialog can not be hidden using the available Task Dialog messages, but it should be possible by using APIs like
I would prefer to disallow text changes while the buttons are part of a currently displayed dialog, and use navigation only when actually setting the When I have some free time, I can try to implement the mentioned changes ( Thank you! |
Great!
I see. These would be framework provided derived classes and there would be no option to implement your own. These buttons should then not use an interface because that can be implemented by user code. It should be an abstract base class with a private constructor and the derived classes sealed. Inheritance is used as a discriminated union type here. It's not for Liskov substitution. Regarding |
My implementation also has a |
I have now done the changes in branch @jnm2 Thanks for your suggestion! You are right, it is more user-friendly to have different properties so that the user doesn't need to concatenate the strings with an The main change that I have done is that I have extracted all properties (and events) that describe the task dialog's contents like The When you change updatable properties
Yes, that's exactly what I had in mind. When the TaskDialog is shown (or navigates), the As long as the The next important change is that I have extracted properties, methods and events that belong to a specific task dialog control (like progress bar, check box, expander) into their own classes, to reduce clutter in the All the control classes inherit from For example, previously you had to use the following code to show an marquee progress bar in the Task Dialog (which is simply the translation of the native API): dialog.ShowMarqueeProgressBar = true;
dialog.Opened += (s, e) =>
{
// Actually enable the marquee.
dialog.SetProgressBarMarquee(true);
};
// or, for navigation, handle the "dialog.Navigated" event Now you can use this code: dialogContents.ProgressBar = new TaskDialogProgressBar()
{
State = TaskDialogProgressBarState.Marquee
}; Or, previously if you wanted to specify a verification checkbox that is initially checked, and later programatically uncheck it (while the dialog is displayed): dialog.VerificationText = "Checkbox Text";
dialog.VerificationFlagCheckedByDefault = true;
dialog.Show();
// Later (while the dialog is shown): Uncheck the box
dialog.ClickVerification(false); Now: var checkbox = dialogContents.VerificationCheckbox = new TaskDialogVerificationCheckbox()
{
Text = "Checkbox Text",
Checked = true
};
dialog.Show();
// Later (while the dialog is shown): Uncheck the box
checkbox.Checked = false; I have also created the Note that because the common button is now represented by its own class, it is no longer possible to specify the common buttons by a flags value like Note: Because radio buttons have a bit different semantics and event types, they just inherit from The There are now the following classes that represents controls (I have removed the interfaces):
Note: I have also removed the properties Instead, to set the default common or custom button, you can simply set its Also, when the This has one small disadvantage however: It's not possible to "uncheck" (clear the selection) of a radio button while the dialog is active. This is reflected in the code by throwing an Generally, I think with the recent commit the API is more user-friendly and .NET/OOP-like. What do you think? Edit: I had to remove the property var dialogContents = new TaskDialogContents()
{
Content = "Before navigation"
};
var dialog = new TaskDialog(dialogContents);
var radioButton = dialogContents.RadioButtons.Add("My Radio 1");
radioButton.Checked = true;
var customButton = dialogContents.CustomButtons.Add("My Custom Button");
customButton.ButtonClicked += (s, e) =>
{
// Create new contents and navigate the dialog, but do
// NOT set e.CancelClose to true. This will cause the dialog to
// close after this handler returns, but specifying the common
// button as result even if is not present in the current dialog's
// GUI (and therefore the current TaskDialogContent's button
// collections) as the resulting button ID by TaskDialogIndirect().
// Also, it will contain the ID of the selected radio button before
// navigation, to which the dialog no longer has access.
var newContents = new TaskDialogContents()
{
MainInstruction = "After navigation",
Content = "Text",
MainIcon = TaskDialogIcon.SecurityShieldGrayBar
};
dialog.CurrentContents = newContents;
// Show a new dialog to delay the close of the outer dialog.
TaskDialog.Show("Close Me");
};
var resultButton = dialog.Show();
Console.WriteLine("Resulting Button: " + resultButton); In this example, when clicking the custom button, the dialog navigates to a new GUI that doesn't have a custom button (and shows another non-modal task dialog to delay the return of the event handler). However, because the event handler doesn't set I fixed the first problem by caching the last button instance with its original ID when the What is also strange, that when running this code a few times, then in some cases I get an Maybe we should track that the dialog was navigated within a |
Sounds very good to me. Maybe we should create a few succinct demos of this new Is there a way to get that native code error fixed? Clearly, this requires a Windows change. If you can create a repro this could be initiated by the team. |
If I might interject a question or two... is the TaskDialog you're designing only meant to be used from code, or would it be possible to at least configure it's properties from the designer (once it's up and running on Core), like ColorDialog, OpenFileDialog, and friends? How would this design affect that use case? |
Hi @GSPP, good idea! I think I can edit my first post and provide the current public API surface along with a few real-world examples of the Task Dialog (e.g. the ones that we use in a commercial application and ones that I have seen on other applications) and their corresponding code for the current API state. I'm sure there still are a lot of TODOs/fine tuning for the API (especially the naming, because often I'm not sure how to name certain things). Edit: I updated my first post to show example usages and the current public API. About the native code error, I will need to check if it can be reproduced directly from a C++ application. However, even with that I don't think the native Windows implementation of the Task Dialog is likely to change in the near future, so I think the best way to fix is to track if the dialog was navigated from within a Hi @gilfusion, thank you, that's a good question! My primary goal to date was to allow the API to be used from code, but we probably should check if we can add support to set its properties from the designer. Unfortunately, I only have little knowledge in that area, so I would appreciate if someone who knows how to do this can give hints what would need to be done in order to support designer scenarios. Thank you! |
@kpreisser For proper designer support, you would need to do the following:
Hope this helps! |
|
Thanks to the help of @wjk and @jnm2, I was able to having the Because the designer initially did not show the A minor issue currently is that while you can edit the button/radio button collections, the code is not generated correctly (adds the button to a new collection instead of the existing one): new KPreisser.UI.TaskDialogCommonButtonCollection().Add(taskDialogCommonButton1);
this.taskDialog1.CurrentContents.ProgressBar.Range.Maximum = 100;
this.taskDialog1.CurrentContents.ProgressBar.Range.Minimum = 0; Edit: This is no longer relevant because the struct has been removed. Another problem (which is probably more important) is that while the designer window shows events of the Additionally, as you can see in the screenshot above, when editing the button collections you cannot add event handlers (e.g. for the Edit: Regarding the |
Thank you for all the hard work on this! I'm going to have the team pick this up and review the implementation more closely. We'll circle back with questions and chat with you about anything we think needs to be polished up before we get to the formal API review with the review board. I'm excited though, this is a great addition! Edited to add: I moved the milestone to 3.0 because I think we can get this reviewed and approved for 3.0. |
Hi @merriemcgaw, thank you very much, that's really good news! As an update, in the meanwhile I found that it would actually be possible to also update the text of custom/radio buttons while the dialog is shown (additionally to the text elements). This not possible using a regular Task Dialog message, but by retrieving the window handles ( I have also seen other implementations/applications that seem to be doing this (e.g. WinSCP when showing a dialog that will automatically close after a few seconds, where the remaining seconds are displayed in the button text). However, this has a few drawbacks:
I have prepared support for updating the button text while the Task Dialog is shown in the code, but disabled it for the above reasons (to enable it, you can uncomment the What do you think? Thanks! |
It's a trade-off between enabling more scenarios and introducing API "quirks" that must be documented. What is the value of those additional scenarios? What are they actually? Why would an application typically update the button texts in a live dialog? |
I've added the notes of API review here. Feedback welcome! |
So, only in 5.0 now? Why so much delay? |
Because further work is required, and it won't be completed before we ship v3.0. |
Hi @terrajobst, thanks a lot for sharing the review notes! I will start to work on addressing them when I have time. |
I have implemented changes to the PR according to the review notes:
Done.
I removed class
I removed Note that this means a When the instance was created by passing an
I think that it is not that simple with However, I think the dicussion also went into the direction of combining
I think the main issue with deriving from It would be possible to add a enum value like Additionally, looking at the implementation (
Therefore, I think Thanks! |
@RussKie is this ready for another pass or do you want to incorporate the feedback from the UX study first? If the latter, we should mark this issue as api-needs-work. |
Updated the label, thanks for the heads up. |
Can this task dialog have textBox in it? Can it be used as an inputBox? |
No, this is an implementation of https://docs.microsoft.com/en-us/windows/desktop/Controls/task-dialogs-overview |
...and use new Windows Forms TaskDialog instead. See: dotnet/winforms#146 Addresses #34
Is there an implementation of this that contains a textbox control, similar to the exception dialog that contains the stack trace with the JIT debugger (below)? I'm working on rolling my own dialog like this to display details (collapsed unless the user clicks a Show Details button) for certain, very specific events in my application, but I'd be just as happy if I didn't have to reinvent something that's already out there. |
@GHosaPhat My understanding is that the underlying Windows API locks it down to only the things shown here: https://docs.microsoft.com/en-us/windows/win32/controls/task-dialogs-overview There might be ways of getting the window handle and adding controls yourself, but I'm not sure it would interoperate well with Windows' layout logic and it would probably be pretty hacky. |
All right. I guess "roll my own" it is, then. Thank you. |
Hi,
On Windows Vista and higher, the Task Dialog is available that provides many more features than a Message Box. While you can show a Message Box in WinForms and WPF, there is no "official" implementation of the Task Dialog yet in .NET WinForms/WPF.
There was an implementation in the Windows API Code Pack 1.1, but it is no longer available/updated, it did not implement all features (like navigation or modifying common/standard buttons), and I believe it had some memory management issues (like not calling
Marshal.DestroyStructure()
after callingMarshal.StructureToPtr()
in order to free allocated strings for custom/radio buttons) and a few other issues.At my company, we currently use the Task Dialog in a (commercial) WPF application to show a marquee progress bar while an operation (like database backup) is running, and then navigate it to one showing a green header to indicate the operation is finished.
Visual Studio is also using a Task Dialog:
Also, the Windows Design Guidelines (Desktop Apps) for Messages and Dialog Boxes show features of the task dialog.
Do you think a Task Dialog could also be added directly to WinForms/WPF? Thank you!
Edit:
Rationale and Usage
The Windows Task Dialog (which is available since Windows Vista) has a lot of configuration options comparing to a regular Message Box, can show additional controls like a progress bar, and supports event handling. However, it has not yet been integrated officially into WinForms/WPF, so if you wanted to use it, you had to implement the native APIs yourself, or use a 3rd party library.
Implementing the Task Dialog directly in WinForms allows users to directly use the Task Dialog in any new WinForms/WPF .NET Core application, just like a MessageBox. You can then either use the simple static
Show()
method (similar to a MessageBox), or you can create an instance of theTaskDialog
, configure itsTaskDialogPage
and then show it.Features of the proposed Task Dialog:
TaskDialogPage.Navigate(TaskDialogPage)
while the dialog is displayedhWnd
) through theHandle
property so that the dialog window can be further manipulated (or used as owner for another window)See also the Task Dialog Demo App for examples.
Show a simple Task Dialog
Dialog similar to the Visual Studio dialog
Show a multi-page dialog that shows current progress, then navigates to a result
See also: Multi-page dialog boxes
Other examples from existing applications
"Save document" dialog from Notepad/Paint/WordPad
Windows 7 Minesweeper Difficulty Selection
Windows Security dialog when trying to access network files
Auto-closing Dialog (closes after 5 seconds)
Proposed API
TODO: Which namespace to use for the types? In the PR I used
System.Windows.Forms
for now.Event Cycle
The events in the proposed API currently have the folowing cycle at runtime (the diagram illustrates navigating the dialog in the
TaskDialogButton.Click
event):Implementation
The proposed API is implemented with PR #1133.
API Updates
Removed propertyTaskDialogContents.DoNotSetForeground
as it doesn't seem to have an effectTaskDialogControlCollection
andTaskDialogButtonCollection
TaskDialogCustomButtonCollection
andTaskDialogRadioButtonCollection
inherit fromCollection
instead ofKeyedCollection
TaskDialogButtons
toTaskDialogCommonButtonCollection
ResultVerificationFlagChanged
fromTaskDialog
ExpandedByDefault
toExpanded
(TaskDialogExpander
) (so its value will be updated when the user clicks the expando button)imageres.dll
)TaskDialog
extendsSystem.ComponentsModel.Component
(and is disposable)Tag
property toTaskDialogControl
TaskDialogCommonButton
now has a default constructor (like other control classes)MainInstruction
->Instruction
,Content
->Text
,ButtonClicked
->Click
)TaskDialogRadioButton
andTaskDialogVerificationCheckbox
has been aligned with WinForms concepts (propertyChecked
, eventCheckedChanged
).TaskDialogVerificationCheckbox
toTaskDialogCheckBox
(along with properties)TaskDialogProgressBarRange
to be used for theTaskDialogProgressBar.Range
property instead of(int min, int max)
for better designer supportTaskDialogContents.DoNotSetForeground
as it is actually working.TaskDialogBooleanStatusEventArgs
.TaskDialogProgressBarState
enum valueMarqueeDisabled
toMarqueePaused
TaskDialogControl
abstract
TaskDialogIcon.Stop
toError
TaskDialogProgressBar.Range
property (along with theTaskDialogProgressBarRange
struct) and instead added propertiesMinimum
andMaximum
directly on theTaskDialogProgressBar
and also renamed propertyPosition
toValue
, to simplify the API and align with the WinForms ProgressBarTaskDialogContents
(FooterText
,FooterIcon
,FooterIconHandle
) into their ownTaskDialogFooter
class. The reasoning for this is that a separate dialog area is added for the footer when it is used (as shown in the below image), similar to the expander (and it reduces the number of properties inTaskDialogContents
).Also, when you intially don't create a footer when showing the task dialog, you cannot later add one by updating the
FooterText
property, similar to theText
property of theExpander
(which is different from the other text properties likeTaskDialogContents.Text
andInstruction
that can be added later).A separate
TaskDialogFooter
class that inherits fromTaskDialogControl
can thus share the behavior withtaskDialogExpander
to throw anInvalidOperationException
when trying to update itsText
property but the control wasn't created because it was initiallynull
(orstring.Empty
).TaskDialog.Closing
toClosed
andTaskDialogContents.Destroying
toDestroyed
, and added a newTaskDialog.Closing
event that is called directly after aTaskDialogButton.Click
event if the button would close the dialog, and it allows to cancel the close (similar toForm.FormClosing
event in WinForms) - see this comment (Option B).TaskDialogExpander.ExpandoButtonClicked
toExpandedChanged
TaskDialogContents
toTaskDialogPage
and propertyTaskDialog.CurrentContents
toTaskDialog.Page
. This is because the documentation also talks about "navigating to a new page" - for example see Multi-page dialog boxes.TimerTick
event onTaskDialogPage
:This event represents the
TDN_TIMER
notification that is called every 200 ms if theTDF_CALLBACK_TIMER
flag was set in the task dialog config. The previous implementation specified the flag if the event handler has been set (like the implementation in the Windows API Code Pack did), but this means you could not add an event handler to theTimerTick
event after the dialog is displayed/navigated. Also, the interval of200
is fixed (and a user might get the impression that the dialog can only be updated every 200 ms, which is not the case).Instead, the user can use one of the already existing UI timer implementations like
System.Windows.Forms.Timer
. Both the Task Dialog timer and the WinForms Timer use a Windows timer (WM_TIMER
messages), so using the WinForms timer should have the same behavior as the TaskDialog timer but with more flexibility.StartupLocation
fromTaskDialogPage
toTaskDialog
because it only has an effect when showing the dialog (but not when navigating it) and therefore isn't related to the page (which represents the contents of the dialog).Added eventsEdit: Removed these again because of an unresolved issue when closing the dialog.TaskDialog.Activated
andTaskDialog.Deactivated
.TaskDialog.Shown
(similar toForm.Shown)
.TaskDialogCommonButton
toTaskDialogStandardButton
(along with collections and property names).TaskDialogPage.DoNotSetForeground
toTaskDialog
because it only has an effect when showing the dialog, but not when navigating it.Icon
+IconHandle
onTaskDialogPage
andTaskDialogFooter
into a singleIcon
property use subclasses to differentiate between icon types (see Windows Task Dialog #146 (comment)).This should avoid confusion about having two mutually exclusive properties (and it allows to initially not showing an icon but then updating it to one using a handle (without using navigation)).
Additionally, it will allow us in the future to add an additional icon type that represents integer/string resource icons (e.g. from
imageres.dll
or the application's executable), which could also be shown using a colored bar (which is not possible when using a handle).TaskDialogPage.CommandLinkMode
toCustomButtonStyle
(along with the enum).TaskDialog
no longer inherits fromSystem.ComponentModel.Component
which was used for trying to implement designer support, but that would require additional work. It be revisited for a future version.TaskDialogPage.Help
toHelpRequest
(and methodOnHelp
toOnHelpRequest
) as discussed in Implement the Windows Task Dialog #1133.TaskDialog.DoNotSetForeground
toTaskDialog.SetToForeground
(the default value is still false), as per the feedback in Implement the Windows Task Dialog #1133.TaskDialog.Show
toShowDialog
.TaskDialogPage.Instruction
toMainInstruction
(same with parameter names for the staticTaskDialog.Show
methods).TaskDialogPage.Title
toCaption
(same with parameter names for the staticTaskDialog.Show
methods).TaskDialogButtonClickedEventArgs
and instead added boolean propertyTaskDialogButton.ShouldCloseDialog
that allows to specify whether clicking the button should close the task dialog.TaskDialogStandardIcon
andTaskDialogIconHandle
, and instead added static fields onTaskDialogIcon
for the standard icons, and added a constructor taking an icon or icon handle.int
indexer toTaskDialogStandardButtonCollection
to avoid an overload resolution in the C# compiler for expressions likepage.StandardButtons[0]
. See Implement the Windows Task Dialog #1133 (comment)string
toTaskDialogFooter
.TaskDialogExpander.ExpandFooterArea
withPosition
(using enum typeTaskDialogExpanderPosition
).TaskDialogPage.BoundDialog
andTaskDialogControl.BoundPage
, so that it is possible e.g. to access the currentTaskDialog
instance in a button click handler. See discussion here.SecurityShield
,SecurityShieldBlueBar
,SecurityShieldGrayBar
,SecurityWarningYellowBar
,SecurityErrorRedBar
,SecuritySuccessGreenBar
toShield
,ShieldBlueBar
,ShieldGrayBar
,ShieldWarningYellowBar
,ShieldErrorRedBar
,ShieldSuccessGreenBar
; as the term "security" would imply that such icons will/must be used for security purposes.TaskDialogPage.CanBeMinimized
toAllowMinimize
.TaskDialogButton.ShouldCloseDialog
toAllowCloseDialog
.TaskDialogStandardButton
andTaskDialogCustomButton
constructors and to theTaskDialogStandardButtonCollection.Add
andTaskDialogCustomButtonCollection.Add
methods.TaskDialogButton.Click
fromEventHandler<EventArgs>
toEventHandler
.StartupLocation
to a parameter ofShowDialog()
- see Set dialog properties viaShowDialog
kpreisser/winforms#16TaskDialogIcon
from aBitmap
- see Simplify setting dialog icon from bitmaps kpreisser/winforms#15TaskDialogPage.MainInstruction
toHeading
- see [minor]MainInstruction
is confusing kpreisser/winforms#6TaskDialogFooter
(and corresponding properties) toTaskDialogFootnote
- see Consider renamingFooter
toFootnote
kpreisser/winforms#8TaskDialogStartupLocation.CenterParent
toCenterOwner
TaskDialogCheckBox.Focus()
TaskDialogButton.ElevationRequired
toShowShieldIcon
TaskDialogCheckBox
(and corresponding properties) to `TaskDialogVerificationCheckBox´ - see Streamlime verification control kpreisser/winforms#18TaskDialogPage.Width
Possible API TODOs
TaskDialogProgressBarState
toTaskDialogProgressBarStyle
Tag
onTaskDialogPage
(which is already present onTaskDialogControl
)ShowDialog()
should behave. Currently, it either shows the dialog modal (when specifying an owner) or non-modal, but in both cases the method does not return until the dialog has closed (similar toForm.ShowDialog()
), which is the behavior of the nativeTaskDialogIndirect
function.This is the same as with
MessageBox.Show()
; however, theMessageBox
automatically uses the current foreground window as owner when you don't specify the owner. For the Task Dialog however, it definitely should be possible to show it non-modal.Note that this means you can show multiple non-modal dialogs at once, but each open dialog will add a
TaskDialog.Show()
entry in the call stack.API Usage Notes
InvalidOperationException
(this was also the behavior of the task dialog implementation in the Windows API Code Pack).For example, radio buttons cannot be unselected while the dialog is shown (but they can be selected). This means that assigning
false
to theTaskDialogRadioButton.Checked
property (while the radio button is shown in a task dialog) will throw.TaskDialogButtonCollection
does not necessarily reflect the order in which the task dialog actually displays the buttons (since common buttons are specified by flags in theTASKDIALOGCONFIG
structure, whereas custom buttons are stored in an array).The native task dialog displays buttons from the collection in the following order:
OK
Yes
No
Abort
Retry
Cancel
Ignore
TryAgain
Continue
Close
Help
TaskDialog.ShowDialog()
can return aTaskDialogButton
which was not added to the dialog in the following cases (which results from the native task dialog implementation):TaskDialogButton.OK
button is returned when the user clicks it.Close()
method, orTaskDialogPage.AllowCancel
has been set and the dialog is closed by the user by pressing ESC or Alt+F4 or clicking the X button in the title bar. In these cases theTaskDialogButton.Cancel
button will be returned.This can also happen when a non-modal task dialog is shown but the main window of the app is closed, in which case the task dialog is also closed and returns a
Cancel
result.imageres.dll
), by initially specifying one of theShield...Bar
icons, and when the dialog is shown, update it to a different icon:However, it isn't possible to use a color bar with a icon handle, because after showing the dialog you can only update the icon member that was initially used to show a dialog, and specifying a color bar requires to use the non-handle icon member.
This means currently you can only use one of the standard icons with a color bar, but in a later version we could add support for showing icons from integer/string resources of DLLs/EXEs (e.g. from
imageres.dll
) (by specifying thehInstance
field of the nativeTASKDIALOGCONFIG
struct), which would then allow you to show a custom icon with a colored bar.The text was updated successfully, but these errors were encountered: