From 000c120223fb3050e5db396938b70d35b80424bf Mon Sep 17 00:00:00 2001 From: David Hall Date: Mon, 30 Jul 2018 16:22:02 -0600 Subject: [PATCH] Added ShellProgressDialog component that wraps IProgressDialog --- WIndows.Forms/Dialogs/ShellProgressDialog.cs | 249 +++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 WIndows.Forms/Dialogs/ShellProgressDialog.cs diff --git a/WIndows.Forms/Dialogs/ShellProgressDialog.cs b/WIndows.Forms/Dialogs/ShellProgressDialog.cs new file mode 100644 index 00000000..5ef150e8 --- /dev/null +++ b/WIndows.Forms/Dialogs/ShellProgressDialog.cs @@ -0,0 +1,249 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Vanara.Extensions; +using static Vanara.PInvoke.Shell32; + +namespace Vanara.Windows.Forms +{ + /// Display style for a . + public enum ShellProgressDialogStyle + { + /// Displays a progress bar that tracks to the progress values. + Normal, + + /// + /// Sets the progress bar to marquee mode. Use this when you wish to indicate that progress is being made, but the time required for + /// the operation is unknown. + /// + Marquee, + + /// + /// Do not display a progress bar. Typically, an application can quantitatively determine how much of the operation remains and + /// periodically pass that value to . The progress dialog box uses this information to + /// update its progress bar. This flag is typically set when the calling application must wait for an operation to finish, but does + /// not have any quantitative information it can use to update the dialog box. + /// + Hidden + } + + /// + /// Wrapper for IProgressDialog which displays a system progress dialog. This object is a generic way to show a user how an operation is + /// progressing. It is typically used when deleting, uploading, copying, moving, or downloading large numbers of files. The dialog is shown + /// on a separate thread and will not block operations in the current thread. + /// + /// This is an example of how to use ShellProgressDialog. + /// + /// private void button1_Click(object sender, System.EventArgs e) + /// { + /// var dlg = new ShellProgressDialog + /// { + /// AutoTimeEstimation = true, + /// CancelMessage = "Canceling. Please wait...", + /// Description = "Processing application...", + /// Title = "MyApp - Long Process" + /// }; + /// dlg.Start(this); + /// for (int i = 0; i < steps.Count; i++) + /// { + /// dlg.Detail = steps[i].Text; + /// steps[1].Action.Invoke(); + /// dlg.UpdateProgress(i + 1, steps.Count); + /// } + /// } + /// + /// + public class ShellProgressDialog : Component + { + private string cancelMsg; + private bool closed = true; + private string description; + private string detail; + private EnumFlagIndexer flags; + private IProgressDialog iDlg; + private string title = string.Empty; + + /// Initializes a new instance of the class. + public ShellProgressDialog() + { + iDlg = new IProgressDialog(); + } + + /// Gets or sets a value indicating whether to automatically estimate the remaining time and display it. + [DefaultValue(false), Category("Behavior"), Description("Automatically estimate the remaining time and display it.")] + public bool AutoTimeEstimation { get => flags[PROGDLG.PROGDLG_AUTOTIME]; set => flags[PROGDLG.PROGDLG_AUTOTIME] = value; } + + /// Gets or sets a message to be displayed if the user cancels the operation. + /// + /// Even though the user clicks Cancel, the application cannot immediately call to close the dialog box. The + /// application must wait until the next time it read to discover that the user has canceled the operation. + /// Since this delay might be significant, the progress dialog box provides the user with immediate feedback by clearing the + /// description and detail lines and displaying this cancel message. The message is intended to let the user know that the delay is + /// normal and that the progress dialog box will be closed shortly. It is typically is set to something like "Please wait while ...". + /// + [DefaultValue(null), Category("Appearance"), Description("Message to be displayed if the user cancels the operation.")] + public string CancelMessage + { + get => cancelMsg; + set + { + if (cancelMsg == value) return; + iDlg.SetCancelMsg(cancelMsg = value); + } + } + + /// Gets or sets a value indicating whether to compact displayed strings if they are too large to fit on a line. + /// to have path strings compacted if they are too large to fit on a line. + [DefaultValue(false), Category("Appearance"), Description("Compact displayed strings if they are too large to fit on a line.")] + public bool CompactStrings { get; set; } + + /// Gets or sets the description that appears on the first line. + /// The description. + [DefaultValue(null), Category("Appearance"), Description("Description that appears on the first line.")] + public string Description + { + get => description; + set + { + if (description == value) return; + iDlg.SetLine(1, description = value, CompactStrings); + } + } + + /// Gets or sets the detail that appears on the second line. + /// The detail. + [DefaultValue(null), Category("Appearance"), Description("Detail text that appears on the second line.")] + public string Detail + { + get => detail; + set + { + if (detail == value) return; + iDlg.SetLine(2, detail = value, CompactStrings); + } + } + + /// Gets or sets a value indicating whether to show the "time remaining" text. + /// Show the "time remaining" text. + [DefaultValue(false), Category("Appearance"), Description("Show the \"time remaining\" text.")] + public bool HideTimeRemaining { get => flags[PROGDLG.PROGDLG_NOTIME]; set => flags[PROGDLG.PROGDLG_NOTIME] = value; } + + /// Checks whether the user has canceled the operation. + /// if the user has canceled the operation; otherwise, . + /// + /// The system does not send a message to the component when the user clicks the Cancel button. You must periodically use this + /// function to poll the progress dialog box object to determine whether the operation has been canceled. + /// + [Browsable(false)] + public bool IsCancelled => iDlg.HasUserCancelled(); + + /// Gets or sets a value indicating whether the Minimize button is displayed in the caption bar of the progress dialog. + /// + /// to display a Minimize button for the dialog; otherwise, . The default is . + /// + [DefaultValue(true), Category("Appearance"), Description("Show the Minimize button in the caption bar")] + public bool MinimizeBox { get => !flags[PROGDLG.PROGDLG_NOMINIMIZE]; set => flags[PROGDLG.PROGDLG_NOMINIMIZE] = !value; } + + /// Gets a value indicating whether this form is displayed modally. + /// if the form is displayed modally; otherwise, . + [DefaultValue(false), Category("Appearance"), Description("Display this form is modally.")] + public bool Modal { get => flags[PROGDLG.PROGDLG_MODAL]; set => flags[PROGDLG.PROGDLG_MODAL] = value; } + + /// Gets or sets a value indicating whether a Cancel button is displayed on the progress dialog. + /// + /// to display a Cancel button on the progress dialog; otherwise, . The default is . + /// + [DefaultValue(true), Category("Appearance"), Description("Display the Cancel button.")] + public bool ShowCancelButton { get => !flags[PROGDLG.PROGDLG_NOCANCEL]; set => flags[PROGDLG.PROGDLG_NOCANCEL] = !value; } + + /// Gets or sets the style of the progress bar on the progress dialog. + /// The style of the progress bar. + [DefaultValue(ShellProgressDialogStyle.Normal), Category("Appearance"), Description("Progress bar style.")] + public ShellProgressDialogStyle Style + { + get => flags[PROGDLG.PROGDLG_NOPROGRESSBAR] ? ShellProgressDialogStyle.Hidden : (flags[PROGDLG.PROGDLG_MARQUEEPROGRESS] ? ShellProgressDialogStyle.Marquee : ShellProgressDialogStyle.Normal); + set + { + switch (value) + { + case ShellProgressDialogStyle.Normal: + flags[PROGDLG.PROGDLG_NOPROGRESSBAR] = false; + flags[PROGDLG.PROGDLG_MARQUEEPROGRESS] = false; + break; + + case ShellProgressDialogStyle.Marquee: + flags[PROGDLG.PROGDLG_NOPROGRESSBAR] = false; + flags[PROGDLG.PROGDLG_MARQUEEPROGRESS] = true; + break; + + case ShellProgressDialogStyle.Hidden: + flags[PROGDLG.PROGDLG_NOPROGRESSBAR] = true; + flags[PROGDLG.PROGDLG_MARQUEEPROGRESS] = false; + break; + + default: + break; + } + } + } + + /// Gets or sets the file dialog box title. + /// The progress dialog box title. The default value is an empty string (""). + [DefaultValue(""), Category("Appearance"), Description("")] + public string Title + { + get => title; + set + { + if (title == value) return; + iDlg.SetTitle(title = value); + } + } + + /// Starts the progress dialog box. + /// The progress dialog box's parent window. This value can be . + public virtual void Start(IWin32Window owner) + { + closed = false; + iDlg.StartProgressDialog(owner?.Handle ?? IntPtr.Zero, dwFlags: flags); + iDlg.Timer(PDTIMER.PDTIMER_RESET); + } + + /// Stops the progress dialog box and removes it from the screen. + public virtual void Stop() + { + if (closed) return; + iDlg.StopProgressDialog(); + closed = true; + } + + /// Updates the progress dialog box with the current state of the operation. + /// + /// An application-defined value that indicates what proportion of the operation has been completed at the time the method was + /// called. If called with this value equaling the value supplied in , the method will be + /// called to close the progress dialog. + /// + /// + /// An application-defined value that specifies what value will have when the operation is complete. + /// + public virtual void UpdateProgress(ulong completed, ulong total) + { + if (closed) return; + iDlg.SetProgress64(completed, total); + if (completed == total) Stop(); + } + + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + return; + if (!closed) + Stop(); + Marshal.FinalReleaseComObject(iDlg); + iDlg = null; + } + } +} \ No newline at end of file