使用活动进度条 C# 解压缩文件

Unzip File with active progress bar C#

我正在尝试创建一个自定义安装程序,以将 zip 文件从固定位置提取到我访问过的用户首选位置,并找到了许多来源,但所有这些都不起作用。 应用程序在解压缩包时冻结并且直到其 100% 完成才更新进度条的问题(我认为不是很有用)

这是我目前所拥有的

void Install()
{
      using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(Constants.UpdateZipPath))
      {
            zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(Zip_ExtractProgress);
            zip.ExtractAll(installDir, ExtractExistingFileAction.OverwriteSilently);
      }
}

void Zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
      if (e.TotalBytesToTransfer > 0)
      {
            ProgressBar.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer);
      }
}

这是我找到的来源之一,但不起作用Extract ZipFile Using C# With Progress Report

当我尝试使用时 Task.Factory.StartNew(() => Install()); 我得到这个错误

Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll
Exception thrown: 'System.InvalidOperationException' in DotNetZip.dll

我正在使用 Ionic.Zip.ZipFile,当我在主线程之外使用它时它不起作用

您不应该直接更新 ProgressBar 值,除非您的代码是 运行 在主线程中。

以下 extension methods 调用必要的辅助例程来规避此限制:

public static class ProgressBarExtensions
{
    //  this extension method can be used for any Control which supports
    //  InvokeRequired() and BeginInvoke()
    public static void EnsureInvokeAsync(this Control control, Action action)
    {
        if (control.InvokeRequired)
        {
            control.BeginInvoke(action);
        }
        else
        {
            action();
        }
    }

    public static void SetProgressValue(this ProgressBar progressBar, int progressValue)
    {
        // 
        // the lambda will capture the argument in a closure
        // the compiler does all the hard work for you
        progressBar.EnsureInvokeAsync(() => progressBar.Value = progressValue);
    }
}

调用示例:

progressBar1.SetProgressValue(myValue);

我确实让它与 Axel Kemper 一起工作,但我必须在开始解压缩之前启动一个线程,这是我想出的代码!

public class MyClass
{
    public bool InstallIsCompleted = false;
    public string CurrentFileBeingExtracted = "";
    public int TotalNumberOfFiles = 1;
    public int NumberOfFilesTransferred = 1;

    public void MyFunction()
    {
        new Thread(Update).Start();
        Task.Factory.StartNew(() => Install());
    }

    void Update()
    {
        while (!InstallIsCompleted)
        {
            Dispatcher.Invoke(() =>
            {
                // updates UI text elements including the display for which file
                // is being extracted and the total progress of the total extraction
                DisplayCurrentFile.Text = CurrentFileBeingExtracted;
                float Percentage = (NumberOfFilesTransferred*100) / TotalNumberOfFiles;
                InstallationProgressBar.Value = Percentage + (SecondProgressBar.Value * (1 / TotalNumberOfFiles));
            });
        }
        //Install Completed
        /* other code here */
    }

    void Install(string ZipPath, string TargetPath)
    {
        using (ZipFile zip = ZipFile.Read(ZipPath))
        {
            // initial setup before extraction
            TotalNumberOfFiles= zip.Entries.Count;
            zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(Zip_ExtractProgress);
            // actual extraction process
            zip.ExtractAll(TargetPath, ExtractExistingFileAction.OverwriteSilently);
            // since the boolean below is in the same "thread" the extraction must 
            // complete for the boolean to be set to true
            InstallIsCompleted = true;
        }
    }

    void Zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
    {
        // must be above 0 to prevent *divide by 0 error
        if (e.TotalBytesToTransfer > 0)
        {
            // If file has completed transfer, update the number of files transferred
            if (Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer) >= 100)
            {
                NumberOfFilesTransferred++;
            }
            // updates the current file being exracted
            CurrentFileBeingExtracted = e.CurrentEntry.FileName.Replace("zip::", "");
            // updates the progress
            ProgressBarExtensions.SetProgressValue(InstallationProgressBar, Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer));
        }
    }
}


public static class ProgressBarExtensions
{
        //  this extension method can be used for any Control which supports
        //  InvokeRequired() and BeginInvoke()
        public static void EnsureInvokeAsync(this Control control, Action action)
        {
            if (control.InvokeRequired)
            {
                control.BeginInvoke(action);
            }
            else
            {
                action();
            }
        }

        public static void SetProgressValue(this ProgressBar progressBar, int progressValue)
        {
            // 
            // the lambda will capture the argument in a closure
            // the compiler does all the hard work for you
            progressBar.EnsureInvokeAsync(() => progressBar.Value = progressValue);
        }
    }

虽然进度计算不是 100% 准确,但它 100% 像我希望的那样工作,因为它将总大小除以文件数,所以 1mb zip 中的 10kb 文件可能是如果只有4个文件占25%完成度

由于某些原因e.TotalBytesToTransfer只计算每个文件,我不确定在提取前是否有可靠的方法来计算提取大小,所以在这种情况下它只能获得提取进度每个文件,并计算有多少个文件