如何从任务列表向GUI报告进度?

时间:2015-04-10 19:22:33

标签: c# multithreading task-parallel-library task ghostscript.net

我目前正在开发一个将.ps(PostScript)文件列表转换为.png的程序。

最初,这是在一个批处理文件中完成的,一次一个文件。我正在处理使用Ghostscript.NET DLL异步处理这些文件的代码。通过将这些分成任务,我将处理时间从30分钟减少到大约6分钟。

我希望能够向用户展示某种进展,以便它看起来不像我的程序被冻结。

我已经足够了解线程以挫败自己,所以非常感谢任何有关最佳方法的建议。下面的代码实现了BackgroundWorker,试图显示进度。我之前使用BGWorker来显示进度,但不是像这样的多个任务。事实上,这是我第一次多线程而不仅仅使用BGWorker。

我觉得BGWorker可能不是我需要使用的东西,但我想在我问之前尝试自己尝试一下。

这是我到目前为止的代码:

public partial class ProcessStatusForm : Form
{
    public string[] testList;
    public string wordPath;
    public string StatusText;
    public GhostscriptVersionInfo _gs_version_info;
    public DirectoryInfo dInfo;
    public List<Task> tasks;
    public float NumberOfTasks;
    public bool PS2PNGRunning;
    public int ProgressPct;
    public float dPercent;
    public decimal decPercent;

    public ProcessStatusForm(string wordDoc, List<string> runList)
    {
        InitializeComponent();
        this.wordPath = wordDoc;
        this.testList = runList.ToArray();
        this.StatusText = string.Empty;
        this._gs_version_info = GhostscriptVersionInfo.GetLastInstalledVersion(GhostscriptLicense.GPL |
            GhostscriptLicense.AFPL, GhostscriptLicense.GPL);
        this.dInfo = new DirectoryInfo(SettingsClass.PSFolder);
        this.PS2PNGRunning = false;
        this.ProgressPct = 0;
        this.NumberOfTasks = runList.Count;
    }

    private void ProcessStatusForm_Shown(object sender, EventArgs e)
    {
        //Spawn tasks for each of the .ps files in the PS_FILES folder
        tasks = new List<Task>(dInfo.GetFiles("*.ps").Length);

        //Start the BackgroundWorker
        this.PS2PNGRunning = true;
        BackgroundWorker.RunWorkerAsync();

        foreach (var file in dInfo.GetFiles("*.ps"))
        {
            //Get fileName to pass fo the ConvertPS2PNG
            string inputFile = file.Name;

            //Create the Task
            var task = Task.Factory.StartNew(() => ConvertPS2PNG(inputFile));
            tasks.Add(task);
        }
        //Wait until all tasks have completed
        Task.WaitAll(tasks.ToArray());
        PS2PNGRunning = false;
    }

    private void ConvertPS2PNG(string input)
    {
        string output = input.Replace(".ps", "_01.png");
        input = SettingsClass.PSFolder + input;
        output = SettingsClass.PNGFolder + output;
        GhostscriptProcessor processor = new GhostscriptProcessor(_gs_version_info, true);
        processor.Process(CreateGSArgs(input, output), new ConsoleStdIO(true, true, true));
    }

    private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        ProgressPct = 0;
        while (PS2PNGRunning)
        {
            Thread.Sleep(1000);
            float TasksCompleted = 0;
            foreach (var tsk in tasks)
            {
                if (tsk.Status == TaskStatus.RanToCompletion)
                {
                    TasksCompleted++;
                }
            }
            StatusText = TasksCompleted + " of " + NumberOfTasks + " converted...";
            dPercent = TasksCompleted / NumberOfTasks;
            dPercent *= 100;
            decPercent = (decimal)dPercent;
            decPercent = Math.Round(decPercent);
            ProgressPct = (int)decPercent;
            BackgroundWorker.ReportProgress(ProgressPct);
        }
        BackgroundWorker.ReportProgress(100);
    }

    private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        this.ProgressLabel.Text = this.StatusText;
        this.progressBar.Style = ProgressBarStyle.Continuous;
        this.progressBar.Value = e.ProgressPercentage;
    }

    public string[] CreateGSArgs(string inPath, string outPath)
    {
        List<string> gsArgs = new List<string>();

        gsArgs.Add("-dBATCH");
        gsArgs.Add("-dNOPAUSE");
        gsArgs.Add("-sDEVICE=png16m");
        gsArgs.Add("-dQUIET");
        gsArgs.Add("-sPAPERSIZE=letter");
        gsArgs.Add("-r800");
        gsArgs.Add("-sOutputFile=" + outPath);
        gsArgs.Add(inPath);

        return gsArgs.ToArray();
    }
}

当我在BackgroundWorker_DoWork的代码中放置中断时,一切看起来都是正确的,但是当它到达BackgroundWorker.ReportProgress()时,它永远不会进入BackgroundWorker_ProgressChanged()方法。

至少,我可以使用progressBar.Style作为选框,当它运行时,用户可以看到该程序正在运行,但报告实际进度将是理想的。

正如我之前所说,我还没有完成大量的线程工作,而且我对这个主题的所有知识都来自Google和StackOverflow。如果有一种完全不同的方式,我会对所有的批评持开放态度。

1 个答案:

答案 0 :(得分:0)

当您从设计器屏幕拖动它时,是否将BackgroundWorker名称赋予该对象?如果不更改您的代码以使用它给出的相应名称(默认应该是backgroundWorker1)。

或......

尝试将发件人对象转换为DoWork方法中的BackgroundWorker对象,并从那里调用ReportProgress()。

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bw = sender as BackgroundWorker;
    if (bw != null)
    {
        bw.ReportProgress(25);
    }
}