C# 进度条未正确更新

本文关键字:更新 | 更新日期: 2023-09-27 17:55:40

我有一个C#的小程序,它可以重命名给定目录中的所有图片并调整其大小。

问题是,即使每个文件都在循环内处理,并且ProgressBar.Increment(1)位于此循环的末尾,它仍然在加载时跳过一些数字,就像它是异步的一样。我不希望这样,我希望它流畅,并准确显示它在哪个文件上,没有任何滞后或延迟。可能出了什么问题?

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);
    }

另外,这是在进度条上显示文本的最佳方式吗?

C# 进度条未正确更新

关于进度条上的文本,这取决于您使用的是WinForms还是WPF。如果是 WPF,我真的会研究在 xaml 中布局某种网格,其中您将 TextBlock 分层到进度栏的一部分上,并使用依赖项属性更新它们。不太确定WinForms,但可能不是.创建图形()。DrawString(),你最好还是得到一个进度条和TextBlock控件,并直接更新它们。

至于顺利更新,可能是因为您没有最短更新时间,而且某些更新可能太快了,以至于似乎"跳过"了它们。若要确保看到每个进度条步骤,请尝试在更新进度条方法中的某个位置插入Thread.Sleep(50)

你为什么不尝试从ProgressBar类继承并自己画东西呢?
假设你使用的是Winforms,这里有一个例子:

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
            };
        }
    }
}

Form中使用此类,而不是默认的ProgressBar类。然后,您只需将进度条的MaximumValue设置为 filesLength 并继续增加 Value 属性,直到它达到最大值,这将是filesLength

基本上,在renameAndResizeFiles()函数中,您会说:

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;
}

这将消除对任何其他功能的需求。

附言为什么你的函数是骆驼座?C# 函数命名约定声明函数将被 PascalCased (有人为此提供链接吗?因此,每个函数名称中的第一个字母都要大写。然而,这只是一个惯例。

我没有开发

BackgroundWorker,而是开发了一个名为SxProgress的类,该类具有非常简单的界面,您可以通过以下方式使用它:

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 ;
}

为了显示进度,该类创建一个带有进度栏和标签的表单,并选择性地处理"停止"按钮以中断该过程。

链接到SxProgress概述和源代码:https://stackoverflow.com/questions/30829395/an-easy-to-use-progress-bar-for-winforms

另请注意,该类为并行进程提供了相同的简单接口,在计算机中创建与内核一样多的线程,并将项目透明地调度到不同的线程:只需将SxProgress.Execute()更改为SxProgress.ExecuteMulti()即可。