如何在执行异步操作期间控制进度栏

时间:2019-07-15 07:38:45

标签: c# winforms asynchronous async-await progress-bar

在为我的项目实施导出util期间,我在上传文件时遇到了阻塞UI的问题。基本上,问题在于异步任务期间我无法更新进度栏。

我已经尝试了几种解决方案。通常,当我调用exportPopUp.ShowDialog()时,它会阻止copyAttachment()的执行,并且整个逻辑在关闭窗体后完成。我决定使用Show(),但是当我这样做时,表单还没有激活(全灰色)

这是我的背景逻辑:

   private void exportButton_Click(object sender, EventArgs e)
    {
        // get files
        int row = reportsDataGrid.CurrentCell.RowIndex;
        if (row >= 0)
        {
            string problemId = reportsDataGrid.Rows[row].Cells[0].Value.ToString();
            AC.Trace.I("Problem Id", problemId);
            FolderBrowserDialog dlgFolderBrowser = new FolderBrowserDialog();
            dlgFolderBrowser.Description = "Select folder to save Report files!";
            DialogResult result = dlgFolderBrowser.ShowDialog();
            if (result == DialogResult.OK)
            {
                string folderName = dlgFolderBrowser.SelectedPath;
                AC.Trace.I("Destination folder name", folderName);
                CIS.PRS.Data.Attachments attachments = jampPrsService.ReportFiles(problemId);
                processAttachments(attachments, folderName, problemId);
            }
        }
    }

    private async void processAttachments(Attachments attachments, string folderName, string problemId)
    {
        this.exportPath = folderName + "\\" + problemId;
        cts = new CancellationTokenSource();
        this.exportPopUp = new exportPopUp(attachments.Size(), cts);
        this.exportPopUp.ExportFinished += ExportPopUp_ExportFinished;
        exportPopUp.setExportLabelText("Exporting problem report " + problemId);
        exportPopUp.ShowDialog();
        await copyAttachments(attachments, folderName, problemId);
    }

    private void ExportPopUp_ExportFinished()
    {
        this.finishExport();
    }

    private async Task copyAttachments(Attachments attachments, string folderName, string problemId)
    {
        //List<Task> tasks = new List<Task>();
        foreach (Attachment attachment in attachments.attachments)
        {
            //tasks.Add(Task.Factory.StartNew(() => copy(attachment, folderName, problemId)));
            await Task.Factory.StartNew(() => copy(attachment, folderName, problemId));
        }
        //await Task.WhenAll(tasks);
    }

    private void copy(Attachment attachment, string folderName, string problemId)
    {
        FileStream fs = null;
        if (!Directory.Exists(exportPath))
        {
            Directory.CreateDirectory(exportPath);
        }
        try
        {
            using (fs = new FileStream(Path.Combine(exportPath, attachment.Name), FileMode.Create))
            {
                fs.WriteAsync(attachment.Data, 0, attachment.Data.Length, this.cts.Token).Wait();
                fs.Flush();
                fs.Close();
                this.exportPopUp.performProgressBarStep();
            }
            AC.Trace.I("File has been saved: ", attachment.Name);
        }

        catch (Exception ex)
        {
            AC.Trace.E("Cannot write file " + attachment.Name, ex);
        }
    }
    private void finishExport()
    {
        this.exportPopUp.Close();
        this.exportPopUp.Dispose();
        MessageBoxCc.ShowInformation("Problem report exported succesfully. \n" +
                "Report exported to '"+ exportPath + "'", "Problem Request", "675");
    }
}

这是我的exportPopUp类:

public delegate void ExportFinishHandler();

    public partial class exportPopUp : Form
    {
        public event ExportFinishHandler ExportFinished;

        private CancellationTokenSource cancellationTokenSource;

        public exportPopUp(int progressBarSize, CancellationTokenSource cancellationTokenSource)
        {
            InitializeComponent();
            this.CenterToScreen();
            this.cancellationTokenSource = cancellationTokenSource;
            this.progressBar.Maximum = progressBarSize;
            this.progressBar.Step = 1;
            this.progressBar.Value = 0;        
        }

        public void setExportLabelText(string text)
        {
            exportLabel.Text = text;
        }

        public void performProgressBarStep()
        {
            this.progressBar.PerformStep();
            MessageBoxCc.ShowInformation("VALUE " + this.progressBar.Value + " MAX " + this.progressBar.Maximum, "KOZA", "123");
            if(this.progressBar.Value == this.progressBar.Maximum)
            {
                this.ExportFinished();
            }
        }

        private void cancelBtn_Click(object sender, EventArgs e)
        {
            cancellationTokenSource.Cancel();
        }
    }

通常,整个逻辑都能按我预期的那样工作,但是我无法同时执行复制任务和更新进度条。预先感谢

更新

更改其预期的功能后,但为了调用导出而不是从导出按钮再次调用其灰色和库存。

我不是通过导出按钮来附加此方法的执行

监听器类:

// Inner listener class 
public class ReportCreatedListener
{
    private frameProblemRequestReport frameProblemRequestReport;

    public ReportCreatedListener(frameProblemRequestReport frameProblemRequestReport)
    {
        this.frameProblemRequestReport = frameProblemRequestReport;
    }

    public async Task notifyRaportCreated(string problemId)
    {
        await this.frameProblemRequestReport.reportCreationFinished(problemId);
    }
}

通话:

    internal async Task reportCreationFinished(string lastProblemId)
    {
        if ((lastProblemId).Contains(report.ReportInfo.ProblemId))
        {
            string problemId = report.ReportInfo.ProblemId;
            string folderName = "C:\\Users\\Z006DQF6\\Desktop";
            AC.Trace.I("Exporting created raport to location: ", folderName);
            CIS.PRS.Data.Attachments attachments = jampPrsService.ReportFiles(lastProblemId);
            await processAttachments(attachments, folderName, problemId);
        }
    }

reportCreationFinished是从另一个侦听器触发的

    private class StateListener : CompoundStateListener
    {
        private JAMPPRSService service;
        public StateListener(JAMPPRSService service)
        {
            this.service = service;
        }
        public async void stateChanged(CompoundModel cm)
        {
            string lastSendReportId = cm.getMember("LastCreatedReportId").getValue().ToString();
            await service.reportCreatedListener.notifyRaportCreated(lastSendReportId);
        }
    }

我无法继续前进,因为此事件来自用Java编写的后端

1 个答案:

答案 0 :(得分:1)

这里的问题是从异步处理切换到同步。您甚至可以在代码中执行两次。

如果从异步等待开始,则需要在整个调用层次中进行绘制。

1)从点击处理程序开始。这应该是层次结构中唯一的async void方法。在这里等待下一个

private async void exportButton_Click(object sender, EventArgs e)
{
    await processAttachments(attachments, folderName, problemId);
}

2)使下一个调用方法返回一个Task,并使用Show,以便copyAttachments之后将执行并可以等待

          return Task here
                |
                v
private async Task processAttachments(Attachments attachments, string folderName, string problemId)
{
    this.exportPath = folderName + "\\" + problemId;
    cts = new CancellationTokenSource();
    this.exportPopUp = new exportPopUp(attachments.Size(), cts);
    this.exportPopUp.ExportFinished += ExportPopUp_ExportFinished;
    exportPopUp.setExportLabelText("Exporting problem report " + problemId);
    exportPopUp.Show();  // <= !
    await copyAttachments(attachments, folderName, problemId);
}

3)使用fs.WriteAsync返回的任务并等待它。再次使copy方法返回一个Task来向上传播它:

private void copy(Attachment attachment, string folderName, string problemId)
{
    ...
    try
    {
        using (fs = new FileStream(Path.Combine(exportPath, attachment.Name), FileMode.Create))
        {
            awaitfs.WriteAsync(attachment.Data, 0, attachment.Data.Length, this.cts.Token);
            fs.Flush();
            fs.Close();
            this.exportPopUp.performProgressBarStep();
        }           
    }
    ...

4)等待复制方法(如果要一个接一个地复制附件):

private async Task copyAttachments(Attachments attachments, string folderName, string problemId)
{
    foreach (Attachment attachment in attachments.attachments)
    {            
        await copy(attachment, folderName, problemId));
    }
}

这应该产生一个可行的解决方案,其中两个表单都将保持响应状态,并且您会看到进度条充满了。