进度条显示与值不匹配

时间:2014-11-16 21:46:59

标签: c# progress-bar

我在Windows 7 64位上使用Visual Studio 2013,尝试制作一个简单的程序来管理某些文件的副本,例如TeraCopy。

我正面临这个问题: 我有一个进度条,显示主要进度(复制文件数/文件总数),显示与值不匹配...

以下是该行为的视频:https://vid.me/tDc1

以下是代码:

_totalProcessFileProgressBar.Minimum = 0;
_totalProcessFileProgressBar.Maximum = FileList.Length;
_totalProcessFileProgressBar.Value = 0;

int fileCounter = 1;
foreach (string File in FileList)
{
    Program.move(File, _destinationFolder.SelectedPath + System.IO.Path.GetFileName(File), (fileCounter++).ToString() + " / " + FileList.Length.ToString());
    Trace.write(File);
    ++_totalProcessFileProgressBar.Value;

    //_totalProcessFileProgressBar.Update();
    //_totalProcessFileProgressBar.Refresh();
    _totalProcessFileProgressBar.Invalidate();
}

顺便说一句,我也尝试过使用它:

_totalProcessFileProgressBar.Step = 1;

(...)

_totalProcessFileProgressBar.PerformStep();

结果完全相同。

在调试模式下,我可以看到进度条的值为4,例如,最大值为8,条形图仅显示30%的进度。

好的,所以我使用了totalProcessFileProgressBar.Maximum = FileList.Length - 1,并将我的Program.move()方法更改为async,行为非常相似。

以下是一些代码:

Program.move():

public async static Task<int> move(String inFileName, String outFileName, string text, bool copy = false)
{
    FileAttributes attr = File.GetAttributes(inFileName);
    if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
    {
        return 0;
    }

    FileStream streamIn = null;

    try
    {
        streamIn = new FileStream(inFileName, FileMode.Open);
    }
    catch (System.IO.IOException ex)
    {
        Trace.write("[IOException]" + inFileName + " : " + ex.Message);
        return 0;
    }
    catch (System.UnauthorizedAccessException ex)
    {
        Trace.write("[UnauthorizedAccessException]" + inFileName + " : " + ex.Message);
        return 0;
    }

    mainForm.currentFileProgressBar.Minimum = 0;
    mainForm.currentFileProgressBar.Maximum = Convert.ToInt32(streamIn.Length);
    mainForm.currentFileProgressBar.Value = 0;
    mainForm.Text = '[' + text + ']';

    BinaryReader inFile = new BinaryReader(streamIn);
    BinaryWriter outFile = new BinaryWriter(new FileStream(outFileName, FileMode.Create));

    Trace.write("Out file name : " + ((FileStream)(outFile.BaseStream)).Name);

    const int BUFFER_LENGHT = 8388608;
    byte[] buffer = new byte[BUFFER_LENGHT];

    int amountRead;

    const int CONVERSION = ((1024 * 1024) / 1000);

    Stopwatch stopwatchCompteurDatas = new Stopwatch();
    stopwatchCompteurDatas.Start();

    long currentFileAmountRead = 0;

    while ((amountRead = inFile.Read(buffer, 0, BUFFER_LENGHT)) > 0)
    {
        outFile.Write(buffer, 0, amountRead);
        dataCounter += amountRead;
        currentFileAmountRead += amountRead;
        mainForm.currentFileProgressBar.Value += amountRead;
        mainForm.currentFileProgressBar.Invalidate();
        using (Graphics gr = mainForm.currentFileProgressBar.CreateGraphics())
        {
            gr.DrawString(Convert.ToString((100 * currentFileAmountRead) / streamIn.Length) + "% - " + inFileName, SystemFonts.DefaultFont, Brushes.Black, new PointF(10, mainForm.currentFileProgressBar.Height / 2 - (gr.MeasureString("45" + "%", SystemFonts.DefaultFont).Height / 2.0F)));
        }

        if (dataCounter > (36870912))
        {
            elapsedTime += stopwatchCompteurDatas.ElapsedMilliseconds;
            stopwatchCompteurDatas.Restart();
            if (elapsedTime > 0)
            {
                double rate = dataCounter / (elapsedTime * CONVERSION);
                mainForm.Text = Convert.ToString(rate) + " Mb/s   [" + text + ']';
            }
            dataCounter = 0;
            elapsedTime = 0;
        }
    }

    inFile.Close();
    outFile.Close();

    return 0;
}

表单类中的一些代码:

// Drag'n drop Callback
void mainForm_DragDrop(object sender, DragEventArgs e)
{
    // Extract the data from the DataObject-Container into a string list
    FileList = (string[])e.Data.GetData(DataFormats.FileDrop, false);

     processFileList();
}


private async void processFileList()
{
    _totalProcessFileProgressBar.Minimum = 0;
    _totalProcessFileProgressBar.Maximum = FileList.Length-1;
    Trace.write("_totalProcessFileProgressBar.Maximum : " + _totalProcessFileProgressBar.Maximum);
    _totalProcessFileProgressBar.Value = 0;
    _totalProcessFileProgressBar.Step = 1;

    if ("" == _destinationFolderSelector.SelectedPath)
    {
        DialogResult result = this._destinationFolderSelector.ShowDialog();

        if(DialogResult.Cancel == result)
        {
            return;
        }
        else
        {
            _destinationFolderSelector.SelectedPath += '\\';
        }
    }

    Trace.write("destinationFolderSelector.SelectedPath : " + _destinationFolderSelector.SelectedPath);

    int filesCounter= 1;
    foreach (string File in FileList)
    {
        this.Text += File + "\n";

        var context = TaskScheduler.FromCurrentSynchronizationContext();
        await Program.move(File, _destinationFolderSelector.SelectedPath + System.IO.Path.GetFileName(File),
                (compteurFichiers++).ToString() + " / " + FileList.Length.ToString());

        //Program.move(File, _destinationFolderSelector.SelectedPath + System.IO.Path.GetFileName(File), (compteurFichiers++).ToString() + " / " + FileList.Length.ToString());
        Trace.write(File);
        //++_totalProcessFileProgressBar.Value;
        _totalProcessFileProgressBar.PerformStep();
        Trace.write("_totalProcessFileProgressBar.Value : " + _totalProcessFileProgressBar.Value + " / " + _totalProcessFileProgressBar.Maximum);

        _totalProcessFileProgressBar.Invalidate();
    }
}

希望你能帮忙解决这个问题...在我身边,我读到了关于异步的文档,我不知道它的工作方式。

2 个答案:

答案 0 :(得分:0)

我建议您使用Task.Run(...)。看起来像无效/重绘相关的错误,因为您在UI线程上完成所有操作。类似的东西:

var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(() =>
    {
        Program.move(File, _destinationFolder.SelectedPath + System.IO.Path.GetFileName(File),
            (fileCounter++).ToString() + " / " + FileList.Length.ToString());
        Trace.write(File);
    }).ContinueWith((task) =>  ++_totalProcessFileProgressBar.Value, context);

希望它有助于推动你朝着正确的方向前进!

答案 1 :(得分:0)

令人遗憾的是,问题中的代码示例并不是更好。更完整的将允许更好的答案。也就是说,我相信您在这里真正需要的是使用async / await模式:

// Making the assumption here that the method is a button's Click event handler
private async void button1_Click(object sender, EventArgs e)
{
    _totalProcessFileProgressBar.Minimum = 0;
    _totalProcessFileProgressBar.Maximum = FileList.Length;
    _totalProcessFileProgressBar.Value = 0;

    int fileCounter = 1;
    foreach (string File in FileList)
    {
        await Task.Run(() => Program.move(File,
            _destinationFolder.SelectedPath + System.IO.Path.GetFileName(File),
            (fileCounter++) + " / " + FileList.Length));
        Trace.write(File);
        ++_totalProcessFileProgressBar.Value;
    }
}

这导致在UI线程继续时在工作线程中处理每个“移动”操作。当每个操作完成时,执行使用UI线程返回紧跟await之后的语句,即Trace.write(File);方法。

根据实际的实施情况,有可能将Program.move()方法设为async方法,允许您放弃Task.Run()来电,只需await Program.move()直接致电。由于给出了不完整的代码示例,我无法提供更具体的建议。

请注意,上述假设Trace.write(File);不是一项耗时的操作。如果是,那么它应该包含在工作线程的任务中,而不是在它之后。或者,如果Trace.write()方法需要访问某个UI对象,那么您也可以将其转换为async方法,使用Task执行耗时的部分。工作线程但仍然访问UI线程中的UI对象。同样,通过更完整的代码示例,可以在此处提供更完整的答案。不幸的是,我所能做的就是尽力解决最明显的可能性。