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);
}
}
}