简体   繁体   中英

C# Progress bar not being updated properly

I have a little program in C#, where it renames and resizes all pictures inside a given directory.

The problem is, that even though every file is being handled inside a loop, and the ProgressBar.Increment(1) is at the end of this loop, it still jumps over some numbers on the loading, it's like its asynchronous. I don't want that, I want it to ne smooth, and to show exactly on which file it is, without any lag or delay. What could be wrong?

private void renameAndResizeFiles() {
        string[] fileEntries = Directory.GetFiles(directory);
        int filesNumber = fileEntries.Length;
        progressBar.Maximum = filesNumber;
        int i = 1;
        foreach (string oldFileNamePath in fileEntries) {
            // handle file work
            i++;
            updateProgressBar(filesNumber, i);
        }
        progressBar.Refresh();
        progressBar.Value = 0;
    }

    private void updateProgressBar(int filesNumber, int i) {
        progressBar.Refresh();
        progressBar.CreateGraphics().DrawString(i + " / " + filesNumber, 
            new Font("Arial", (float) 10.0, FontStyle.Regular), 
            Brushes.Black, 
            new PointF(progressBar.Width / 2 - 10, progressBar.Height / 2 - 7));
        progressBar.Increment(1);
    }

Also, is this the best way to show text over a progress bar?

Regarding the text on progress bar, it depends if you're using WinForms or WPF. If WPF, I would really look into layout out some kind of grid in xaml where you layer a TextBlock over part of your progress bar, and update them using DependencyProperties. Not so sure about WinForms, but probably not .CreateGraphics().DrawString(), you're still probably better off to get a ProgressBar and TextBlock control in place and update them each directly.

As for the smooth updating, it's likely because you don't have a minimum update time, and some updates may be so fast it seemingly 'skips' right past them. To ensure you see each progress bar step, try inserting a Thread.Sleep(50) somewhere in your update progress bar method.

Why don't you try inheriting from the ProgressBar class and draw stuff on your own?
Assuming you're using Winforms , here's an example :

using System;
using System.Drawing;
using System.Windows.Forms;

namespace TestProgressBar
{
    public class MyProgressBar : ProgressBar
    {
        public MyProgressBar() : base()
        {
            Draw += (sender, e) =>
            {
                e.Graphics.DrawString(Value + " / " + MaximumValue, new Font("Arial", (float) 10.0, FontStyle.Regular), Brushes.Black, new PointF(Width / 2 - 10, Height / 2 - 7));//you can also use the Font property as the font
            };
        }
    }
}

Use this class in your Form rather than the default ProgressBar class. And then, you would just have to set the MaximumValue of the progress bar to filesLength and just keep increasing the Value property till it reaches the maximum value, which would be filesLength .

Basically, in the renameAndResizeFiles() function, you would say :

public void renameAndResizeFiles()
{
    string[] fileEntries = Directory.GetFiles(directory);
    int filesNumber = fileEntries.Length;
    myProgressBar.Maximum = filesNumber;
    int i = 1;
    foreach (string oldFileNamePath in fileEntries) {
        // handle file work
        progressBar.Value = i;
        i++;
    }
    progressBar.Value = 0;
}

Which would remove the need for anyother function.

PS Why are your functions camelCased? C# function naming convention states that functions are to be PascalCased (someone provide a link for this please?). So the first letter in every function name is to be capitalized. Its just a convention however.

Instead of BackgroundWorker, I developed a class named SxProgress with a very simple interface that you may use in the following way :

string[] fileEntries = Directory.GetFiles(directory) ;
object[] UserObjects = new object[] { fileEntries } ;  
SxProgress.Execute("Processing files",fileEntries.Length,
                   true,  // display labels, i.e. "12/345"
                   false, // no stop button
                   renameAndResizeFiles_ExecInThread,UserObjects) ;

private bool renameAndResizeFiles_ExecInThread(int ItemIndex,object[] UserObjects) 
{
   string[] fileEntries = (string[])UserObjects[0] ;
   string filetoprocess = fileEntries[ItemIndex] ;  
   // handle file work here
   return true ;
}

To show the progress, the class creates a form with ProgressBar and Labels and optionally handles a "Stop" button to interrupt the process.

link to SxProgress overview and source code : https://stackoverflow.com/questions/30829395/an-easy-to-use-progress-bar-for-winforms

Note also that the class provides the same easy interface for parallel process, creating as many threads as cores in the computer, with transparent dispatch of the items to the different threads: Just change SxProgress.Execute() by SxProgress.ExecuteMulti() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM