使用活动进度栏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)
    {
        // https://stackoverflow.com/a/21969844/1911064
        // 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)
        {
            // https://stackoverflow.com/a/21969844/1911064
            // 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仅占每个文件的数量,我不确定在提取之前有可靠的方法来计算提取大小,因此在这种情况下,只能获取每个文件的提取进度,并计算出有多少文件