【问题标题】:UI unresponsive + progress bar not working while using backgroundworker c#UI无响应+进度条在使用backgroundworker c#时不起作用
【发布时间】:2017-11-19 17:47:56
【问题描述】:

我正在 Visual Studio 中开发 WPF 应用程序,我需要下载一个大文件并将其提取到我的代码中。有人建议我使用后台工作人员,但现在当我尝试增加进度条上的值时,它不起作用......有人可以帮忙吗?

public void InstallVersion(string version)
    {
        string location = File.ReadAllText(@"C:\Users\" + Environment.UserName + @"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
        location = location + @"\Versions\" + version;

        if (File.Exists(location + ".zip"))
            File.Delete(location + ".zip");

        if (Directory.Exists(location))
        {
            DirectoryInfo di = new DirectoryInfo(location);

            foreach (FileInfo file in di.GetFiles())
            {
                file.Delete();
            }
            foreach (DirectoryInfo dir in di.GetDirectories())
            {
                dir.Delete(true);
             }
        }


        if (!myWorker.IsBusy)
        {
            myWorker.RunWorkerAsync();
        }
    }

这是我的工作代码

public MainWindow()
    {
        InitializeComponent();

        myWorker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
        myWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
        myWorker.ProgressChanged += new ProgressChangedEventHandler(myWorker_ProgressChanged);
        myWorker.WorkerReportsProgress = true;
        myWorker.WorkerSupportsCancellation = true;
    }

    protected void myWorker_DoWork(object sender, DoWorkEventArgs e)
    {

        string location = File.ReadAllText(@"C:\Users\" + Environment.UserName + @"\AppData\Roaming\MidnightFallsLauncher\data\locator.txt");
        location = location + @"\Versions\" + Version;

        WebClient Client = new WebClient();
        string url = "";
        string content = "";
        string downloadlink = "";

        List<string> availibleVersions = new List<string>();
        List<string> versionDownload = new List<string>();

        url = "https://midnightfalls.glitch.me/versions.html";
        content = Client.DownloadString(url);

        foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
        {
            if (line.Contains("0"))
            {
                availibleVersions.Add(line);
            }

        }

        url = "https://midnightfalls.glitch.me/versionslink.html";
        content = Client.DownloadString(url);

        foreach (string line in content.Split(new string[] { "<br>", "<br />" }, StringSplitOptions.None))
        {
            if (line.Contains("https"))
            {
                versionDownload.Add(line);
            }

        }

        for (var i = 0; i < availibleVersions.Count; i++)
        {
            if (availibleVersions[i] == Version)
            {
                downloadlink = versionDownload[i];
            }
        }


        Client.DownloadFile(downloadlink, location + ".zip");

        ZipFile.ExtractToDirectory(location + ".zip", location);
        File.Delete(location + ".zip");

        RunGame(Version);
    }

    protected void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {

    }

    protected void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.Dispatcher.Invoke(() =>
        {
            progress.Value += 10;
        });
    }

另外,当我的工作人员运行时,UI 冻结...我很确定这不会发生。

编辑: 用户界面正在更新,但进度条仍然不起作用...

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    看起来问题是主线程一直在工作,而后台工作人员正在工作:

     while (this.myWorker.IsBusy)
     {
        this.Dispatcher.Invoke(() =>
        {
            progress.Value += 10;
        });
    }
    

    意味着您的主线程在后台作业工作时不断地做一些事情,这就是 UI 不更新的原因。

    您需要将进度更新移动到后台工作人员(您还可以在其中设置一个实际上有意义的值,例如指示您下载了多少 'availableVersion')。

    希望这是有道理的。

    编辑:

    假设我们将所有代码直接放在视图中,那么假设我们有一个名为“progressBar”的进度条和一个名为“btnStart”的按钮(由后台工作程序启动)。

    这是代码隐藏:

    private BackgroundWorker worker;
    
     public MainWindow()
     {
        InitializeComponent();
        this.worker = new BackgroundWorker();
    
        this.worker.DoWork += new DoWorkEventHandler(myWorker_DoWork);
        this.worker.RunWorkerCompleted += new 
        RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
        this.worker.ProgressChanged += new 
        ProgressChangedEventHandler(myWorker_ProgressChanged);
        this.worker.WorkerReportsProgress = true;
        this.worker.WorkerSupportsCancellation = true;
    }
    
    private void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.progressBar.Value = e.ProgressPercentage;
    }
    
    private void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
         // Whatever you need to do when finished here (alert, update a label, etc.)
    }
    
    private void myWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        // Just loop and report new progress. Sleep a little in between each
        // progress update so that it isn't over before we have a chance to see it.
        for(int i=0;i<100;i++)
        {
            Thread.Sleep(200);
            this.worker.ReportProgress(i);
        }
    }
    
    private void btnStart_Click(object sender, RoutedEventArgs e)
    {
        this.worker.RunWorkerAsync();
    }
    

    后台工作人员会触发一个事件,通知进度已更改。 主线程拥有该事件的句柄,并更新进度条。 由于它是后台工作人员,因此您不需要使用 Dispatcher.Invoke - 这已经处理好了。

    希望这个例子能为你澄清事情。

    【讨论】:

    • 不客气。问题中的代码是更新的代码吗?还是您将进度更新移至后台工作人员?另外,你看到我对另一个答案的评论了吗?
    • 好吧,你实际上并没有更新你的工人的进度。我将使用一个小的工作示例来编辑我的帖子。
    • 不用担心。我要补充一点,就像 Daniel Mann 提到的那样,如果您运行的是较新版本的 .NET (>=4.5),则应该研究异步等待模式。它确实让事情变得更容易,代码也更干净。
    【解决方案2】:

    我很确定你在这里阻止:

    while (this.myWorker.IsBusy)
    {
        this.Dispatcher.Invoke(() =>
        {
            progress.Value += 10;
        });
    }
    

    您应该在您的myWorker_DoWork 方法中对您的BackgroundWorker 实例调用ReportProgress

    此外,如果您使用的是 .NET 4.5 或更高版本,您可以完全转储 BackgroundWorker 并使用 async/await 模式重写此代码。

    【讨论】:

    • 谢谢,它解冻了 UI,但进度条仍然没有改变
    • 您可能过于频繁地添加值。在您的原始代码中,您将立即达到 100 的进度值,这意​​味着它永远不会改变。还要确保你没有太频繁地调用它,这意味着你仍然通过强制它工作来阻塞 UI 线程。
    • @WhoKnows On Stack Overflow,一旦您的原始问题得到解答,您应该接受一个答案,然后如果您有其他问题,请提出一个新问题。
    猜你喜欢
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多