using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Threading; using Vanara.PInvoke; using static Vanara.PInvoke.Shell32; namespace Vanara.Windows.Shell { /// The Shell's progress dialog. /// public class ShellFileOperationDialog : Component { internal IOperationsProgressDialog iProgressDialog; private ShellItem currentItem; private long currentItems; private long currentProgress; private long currentSize; private ShellItem destItem; private OperationMode mode; private OperationType operation; private ShellItem sourceItem; private long totalItems = 100; private long totalProgress = 100; private long totalSize = 100; /// Initializes a new instance of the class. public ShellFileOperationDialog() { } /// Initializes a new instance of the class. /// The container. public ShellFileOperationDialog(IContainer container) : this() { container.Add(this); } /// Provides operation status flags for ShellFileOperationDialog. public enum DialogStatus { /// The dialog has not been started. NotStarted = 0, /// Operation is running, no user intervention. Running = PDOPSTATUS.PDOPS_RUNNING, /// Operation has been paused by the user. Paused = PDOPSTATUS.PDOPS_PAUSED, /// Operation has been canceled by the user - now go undo. Cancelled = PDOPSTATUS.PDOPS_CANCELLED, /// Operation has been stopped by the user - terminate completely. Stopped = PDOPSTATUS.PDOPS_STOPPED, /// Operation has gone as far as it can go without throwing error dialogs. Errors = PDOPSTATUS.PDOPS_ERRORS, } /// Flags used in Mode [Flags] public enum OperationMode { /// Use the default progress dialog operations mode. Default = PDMODE.PDM_DEFAULT, /// The operation is running. Running = PDMODE.PDM_RUN, /// The operation is gathering data before it begins, such as calculating the predicted operation time. Starting = PDMODE.PDM_PREFLIGHT, /// The operation is rolling back due to an Undo command from the user. Undoing = PDMODE.PDM_UNDOING, /// Error dialogs are blocking progress from continuing. BlockedByErrors = PDMODE.PDM_ERRORSBLOCKING, /// The length of the operation is indeterminate. Do not show a timer and display the progress bar in marquee mode. Indeterminate = PDMODE.PDM_INDETERMINATE, } /// Describes an action being performed that requires progress to be shown to the user using progress dialog. public enum OperationType { /// No action is being performed. None = SPACTION.SPACTION_NONE, /// Files are being moved. Moving = SPACTION.SPACTION_MOVING, /// Files are being copied. Copying = SPACTION.SPACTION_COPYING, /// Files are being deleted. Recycling = SPACTION.SPACTION_RECYCLING, /// A set of attributes are being applied to files. ApplyingAttributes = SPACTION.SPACTION_APPLYINGATTRIBS, /// A file is being downloaded from a remote source. Downloading = SPACTION.SPACTION_DOWNLOADING, /// An Internet search is being performed. SearchingInternet = SPACTION.SPACTION_SEARCHING_INTERNET, /// A calculation is being performed. Calculating = SPACTION.SPACTION_CALCULATING, /// A file is being uploaded to a remote source. Uploading = SPACTION.SPACTION_UPLOADING, /// A local search is being performed. SearchingFiles = SPACTION.SPACTION_SEARCHING_FILES, /// Windows Vista and later. A deletion is being performed. Deleting = SPACTION.SPACTION_DELETING, /// Windows Vista and later. A renaming action is being performed. Renaming = SPACTION.SPACTION_RENAMING, /// Windows Vista and later. A formatting action is being performed. Formatting = SPACTION.SPACTION_FORMATTING, /// Windows 7 and later. A copy or move action is being performed. CopyMoving = SPACTION.SPACTION_COPY_MOVING, } /// The operation can be undone in the dialog. (The Stop button becomes Undo) [DefaultValue(false)] public bool AllowUndo { get; set; } /// /// A ShellItem that represents the item currently being operated on by the operation engine. This property is only used in Windows /// 7 and later. In earlier versions, this property should be /// public ShellItem CurrentItem { get => currentItem; set { currentItem = value; UpdateLocations(); } } /// A ShellItem that represents the target Shell item. public ShellItem Destination { get => destItem; set { destItem = value ?? throw new ArgumentNullException(nameof(Source)); UpdateLocations(); } } /// Gets the elapsed time. /// The elapsed time, accurate to milliseconds. public TimeSpan ElapsedTime { get { ulong t = 0; if (CanProcess) try { iProgressDialog.GetMilliseconds(out t, out _); } catch { } return TimeSpan.FromMilliseconds(t); } } /// Don't display the path of destination file in progress dialog [DefaultValue(false)] public bool HideDestinationPath { get; set; } /// Don't display the location line in the progress dialog [DefaultValue(false)] public bool HideLocations { get; set; } /// Don't display the path of source file in progress dialog [DefaultValue(false)] public bool HideSourcePath { get; set; } /// Gets or sets progress dialog operations mode. /// The mode. public OperationMode Mode { get => mode; set { if (mode == value) return; mode = value; if (CanProcess) iProgressDialog.SetMode((PDMODE)mode); } } /// Sets which progress dialog operation is occurring, and whether we are in pre-flight or undo mode. /// Specifies operation. See . public OperationType Operation { get => operation; set { if (operation == value) return; operation = value; if (CanProcess) iProgressDialog.SetOperation((SPACTION)operation); } } /// Total points, used for showing progress in points. [DefaultValue(100)] public long ProgressBarMaxValue { get => totalProgress; set { totalProgress = value; UpdateProgress(); } } /// Current points, used for showing progress in points. [DefaultValue(0)] public long ProgressBarValue { get => currentProgress; set { currentProgress = value; UpdateProgress(); } } /// Specifies total items, used for showing progress in items. [DefaultValue(100)] public long ProgressDialogItemsMaxValue { get => totalItems; set { totalItems = value; UpdateProgress(); } } /// Current items, used for showing progress in items. [DefaultValue(0)] public long ProgressDialogItemsValue { get => currentItems; set { currentItems = value; UpdateProgress(); } } /// Total size in bytes, used for showing progress in bytes. [DefaultValue(100)] public long ProgressDialogSizeMaxValue { get => totalSize; set { totalSize = value; UpdateProgress(); } } /// Current size in bytes, used for showing progress in bytes. [DefaultValue(0)] public long ProgressDialogSizeValue { get => currentSize; set { currentSize = value; UpdateProgress(); } } /// Gets the remaining time. /// The remaining time, accurate to milliseconds. public TimeSpan RemainingTime { get { ulong t = 0; if (CanProcess) try { iProgressDialog.GetMilliseconds(out _, out t); } catch { } return TimeSpan.FromMilliseconds(t); } } /// Add a pause button (operation can be paused) [DefaultValue(false)] public bool ShowPauseButton { get; set; } /// A ShellItem that represents the source Shell item. public ShellItem Source { get => sourceItem; set { sourceItem = value ?? throw new ArgumentNullException(nameof(Source)); UpdateLocations(); } } /// Gets operation status for progress dialog. /// The operation status. See . public DialogStatus Status => (DialogStatus)(CanProcess ? iProgressDialog.GetOperationStatus() : 0); private bool CanProcess => iProgressDialog != null; private OPPROGDLGF DialogFlags => (ShowPauseButton ? OPPROGDLGF.OPPROGDLG_ENABLEPAUSE : 0) | (AllowUndo ? OPPROGDLGF.OPPROGDLG_ALLOWUNDO : 0) | (HideSourcePath ? OPPROGDLGF.OPPROGDLG_DONTDISPLAYSOURCEPATH : 0) | (HideDestinationPath ? OPPROGDLGF.OPPROGDLG_DONTDISPLAYDESTPATH : 0) | (HideLocations ? OPPROGDLGF.OPPROGDLG_DONTDISPLAYLOCATIONS : 0); /// Pauses progress dialog timer. public void PauseTimer() { if (CanProcess) iProgressDialog.PauseTimer(); } /// Resets progress dialog timer to 0. public void ResetTimer() { if (CanProcess) iProgressDialog.ResetTimer(); } /// Resumes progress dialog timer. public void ResumeTimer() { if (CanProcess) iProgressDialog.ResumeTimer(); } /// Starts the specified progress dialog. /// /// A value that represents the window of the owner window for the common dialog box. This value can be . /// public void Start(HWND owner = default) { iProgressDialog = new IOperationsProgressDialog(); iProgressDialog.StartProgressDialog(owner, DialogFlags); iProgressDialog.SetOperation((SPACTION)operation); iProgressDialog.SetMode((PDMODE)mode); UpdateLocations(); UpdateProgress(); } /// Stops current progress dialog. public void Stop() { if (!CanProcess) return; iProgressDialog.StopProgressDialog(); Thread.Sleep(500); iProgressDialog = null; } /// /// Releases the unmanaged resources used by the and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { Stop(); base.Dispose(disposing); } private void UpdateLocations() { if (CanProcess) iProgressDialog.UpdateLocations(sourceItem.IShellItem, destItem.IShellItem, currentItem?.IShellItem); } private void UpdateProgress() { if (CanProcess) iProgressDialog.UpdateProgress((ulong)currentProgress, (ulong)totalProgress, (ulong)currentSize, (ulong)totalSize, (ulong)currentItems, (ulong)totalItems); } } }