如何在Windows窗体C#中实现进度条?

时间:2013-09-24 02:02:55

标签: c# winforms progress-bar backgroundworker implementation

我有自己的解决方案,可以在我的Windows窗体应用程序中导入月度销售数据。当用户单击import按钮时,程序实际上正在运行,但看起来它没有响应。这个过程需要很长时间,大约需要5分钟。

因此,我想实现一个带有状态条标签的进度条,以显示为用户界面,让用户知道完成任务的程度。这也是我第一次在程序中使用进度条。所以,我阅读了一些教程,展示了如何使用它。有些人使用进度条与后台工作者和计时器。

但我不明白我应该在哪里使用我的解决方案。在后台工作者DoWork()事件中?我不想通过滥用进度条来伪造它,例如设置progressBar.Maximum = 100,progressBar.Value = 0并且只要计时器正在滴答将值增加5.进度条必须报告实际进度而程序正在运行。

以下是我现在用来导入数据的解决方案:

private void btnImport_Click(object sender, EventArgs e)
    {
        if (lsbxBrowsedFiles.Items.Count != 0)
        {
            ArrayList salesHeaderArr = new ArrayList();
            ArrayList salesDetailArr = new ArrayList();

            int i = 0;
            while (i < browsedXmlFileList.Count)
            {
                if (browsedXmlFileList[i].ToUpper().EndsWith("SALESHEADER.XML"))
                {
                    salesHeaderArr.Add(browsedXmlFileList[i]);
                }
                if (browsedXmlFileList[i].ToUpper().EndsWith("SALESDETAIL.XML"))
                {
                    salesDetailArr.Add(browsedXmlFileList[i]);
                }
                i++;
            }

            if (selectedFileIsNotInDestinationFolder(salesHeaderArr, salesDetailArr) == true)
            {
                i = 0;
                while (i < salesHeaderArr.Count)
                {
                    SalesHeader salesHeader = new SalesHeader();
                    string sourceFilePath = salesHeaderArr[i].ToString();
                    readXMLFiles(sourceFilePath, SALES_HEADER);
                    SalesHeader salesCheck = (SalesHeader)salesHeaderList[0];
                    string checkOutletCode = salesCheck.OutletCode;
                    DateTime checkBusDate = salesCheck.BusinessDate.Value;
                    if (SalesHeader.IsThisRowAlreadyImportedInSalesHeader(checkOutletCode, checkBusDate) == false)
                    {
                        salesHeader.ImportSalesHeader(salesHeaderList);
                        salesHeader.CreateImportDataLog(getDestinationFilePath(sourceFilePath),
                            DateTime.Now, salesHeaderList.Count, SALES_HEADER);
                    }
                    else
                    {
                        string errorDate = checkBusDate.ToString("dd MMMM, yyyy");
                        MessageBox.Show("Selected XML File with BusinessDate: " + errorDate + " has been already imported.",
                            "ABC Cafe Import Sales Wizard");
                        MessageBox.Show("Please select a file which has not been imported!",
                            "ABC Cafe Import Sales Wizard");
                        return;
                    }
                    MoveXMLFiletoDestinationFolder(sourceFilePath);
                    i++;
                }
                i = 0;
                while (i < salesDetailArr.Count)
                {
                    SalesDetail salesDetail = new SalesDetail();
                    string sourceFilePath = salesDetailArr[i].ToString();
                    readXMLFiles(sourceFilePath, SALES_DETAIL);
                    SalesDetail salesCheck = (SalesDetail)salesDetailList[0];
                    string checkOutletCode = salesCheck.OutletCode;
                    DateTime checkBusDate = salesCheck.BusinessDate.Value;
                    if (SalesDetail.IsThisRowAlreadyImportedInSalesDetail(checkOutletCode, checkBusDate) == false)
                    {
                        salesDetail.ImportSalesDetail(salesDetailList);
                        salesDetail.GenerateCarryForward(salesDetailList);
                        salesDetail.CalculateImportInventoryBalance(salesDetailList);
                        salesDetail.CreateImportDataLog(getDestinationFilePath(sourceFilePath), DateTime.Now, salesDetailList.Count, SALES_DETAIL);
                    }
                    else
                    {
                        string errorDate = checkBusDate.ToString("dd MMMM, yyyy");
                        MessageBox.Show("Selected XML File with BusinessDate: " + errorDate + " has been already imported.",
                            "ABC Cafe Import Sales Wizard");
                        MessageBox.Show("Please select a file which has not been imported!",
                            "ABC Cafe Import Sales Wizard");
                        return;
                    }
                    MoveXMLFiletoDestinationFolder(sourceFilePath);
                    i++;
                }
                MessageBox.Show("Import has been successfully completed!",
                "ABC Cafe Import Sales Wizard");
                clearListBoxItems();
                lblMessage.Visible = false;
            }
            //Abort the import operation here!
            else
            {
                MessageBox.Show("Please select a file which has not been imported!",
                "ABC Cafe Import Sales Wizard");
                clearListBoxItems();
                lblMessage.Visible = false;
            }
        }
        else
        {
            MessageBox.Show("Please select XML files to import!", 
                "ABC Cafe Import Sales Wizard");
        }
    }

非常感谢任何帮助!

3 个答案:

答案 0 :(得分:3)

这就是我在我的应用程序中使用Progress Bar的方法。

private void btnNext_Click(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
        worker.WorkerReportsProgress = true;
        worker.ProgressChanged += (se, eventArgs) => {
            this.progressBar.Maximum = 100;
            this.progressBar.Minimum = 0;
            this.progressBar.Value = eventArgs.ProgressPercentage;
            lblStatus.Text = eventArgs.UserState as String;
            lblPercentage.Text = String.Format("Progress: {0} %", eventArgs.ProgressPercentage);
        };

worker.DoWork += (se, eventArgs) => {
    int progress = 0;
    ((BackgroundWorker)se).ReportProgress(progress, "Initializing the files...");

    //Process that takes a long time
    //Formula to calculate Progress Percentage 
    //This is how I calculated for my program. Divide 100 by number of loops you have

    int findPercentage = ((i + 1) * 100) / salesHeaderArr.Count;
    progress = 0;
    progress += findPercentage / 2;

    //Report back to the UI
    string progressStatus = "Importing Sales Header... (" + getSourceFileName(sourceFilePath) + ")";     
    ((BackgroundWorker)se).ReportProgress(progress, progressStatus);

   //After Iterating through all the loops, update the progress to "Complete"
   ((BackgroundWorker)se).ReportProgress(100, "Complete...");
};
worker.RunWorkerCompleted += (se, eventArgs) =>
        {
            //Display smth or update status when progress is completed
            lblStatus.Location = new Point(20, 60);
            lblStatus.Text = "Your import has been completed. \n\nPlease Click 'Finish' button to close the wizard or \n'Back' button to go back to the previous page.";
            lblPercentage.Visible = false;
            progressBar.Visible = false;
            btnBack.Enabled = true;
            btnFinish.Enabled = true;
        };

        worker.RunWorkerAsync();
}

答案 1 :(得分:1)

你的程序有很多循环,因为在每次循环迭代中很难获得增量值并增加进度条值。你可以将进度条最大值设置为100,然后将100除以你拥有的循环次数说X。

所以诀窍就是在每个循环完成时用这个值填充进度条

是的,你应该把这段代码放在backgroundworker的DoWork()中,否则它会冻结表格。也不需要计时器。

答案 2 :(得分:-1)

您可以在处理时使用ManualResetEvent,现在让Progressbar增加直到达到某一点并等待Set。

示例:

你有这2个字段

private int progress = 0;
private ManualResetEvent reset = new ManualResetEvent(false); 
// Sets it to unsignalled
private ManualResetEvent reset2 = new ManualResetEvent(false);

while(progress  < 40)
{
   progress ++;
}

reset.WaitOne();

while(progress < 90)
{
   progress ++;
}

reset2.WaitOne();

while(progress < 100)
{
   progress ++;
}

//这完成了进度,现在你的实际工作,你必须发出那些等待的信号。

DoWork()
{
   // long process here. . . .

   reset.Set();

   // Another long process

   reset2.Set();
}