C# 进度条未正确更新
C# Progress bar not being updated properly
我有一个 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);
}
此外,这是在进度条上显示文本的最佳方式吗?
关于进度条上的文字,这取决于您使用的是 WinForms 还是 WPF。如果是 WPF,我真的会研究 xaml 中的某种网格布局,在其中将 TextBlock 层叠在进度条的一部分上,并使用 DependencyProperties 更新它们。对 WinForms 不是很确定,但可能不是 .CreateGraphics().DrawString(),您最好还是获得 ProgressBar 和 TextBlock 控件并直接更新它们。
至于更新流畅,可能是因为你没有规定最短更新时间,有的更新速度可能很快'skips'就过去了。为确保您看到每个进度条步骤,请尝试在更新进度条方法中的某处插入 Thread.Sleep(50)
。
你为什么不尝试继承 ProgressBar
class 并自己绘制东西?
假设您使用的是 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
中使用这个 class 而不是默认的 ProgressBar
class。
然后,你只需要将进度条的 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;
}
这将消除对任何其他功能的需求。
P.S。为什么你的函数是驼峰式的? C# 函数命名约定规定函数是 PascalCased(有人为此提供 link 吗?)。所以每个函数名的第一个字母都要大写。然而,这只是一个惯例。
我开发了一个名为 SxProgress 的 class 而不是 BackgroundWorker,它具有非常简单的界面,您可以按以下方式使用它:
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 ;
}
为了显示进度,class 创建了一个带有 ProgressBar 和标签的表单,并可选择处理 "Stop" 按钮来中断进程。
link SxProgress 概述和源代码:
另请注意,class 为并行处理提供了相同的简单接口,创建与计算机中的内核一样多的线程,并将项目透明地分派到不同的线程:
只需将 SxProgress.Execute()
更改为 SxProgress.ExecuteMulti()
。
我有一个 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);
}
此外,这是在进度条上显示文本的最佳方式吗?
关于进度条上的文字,这取决于您使用的是 WinForms 还是 WPF。如果是 WPF,我真的会研究 xaml 中的某种网格布局,在其中将 TextBlock 层叠在进度条的一部分上,并使用 DependencyProperties 更新它们。对 WinForms 不是很确定,但可能不是 .CreateGraphics().DrawString(),您最好还是获得 ProgressBar 和 TextBlock 控件并直接更新它们。
至于更新流畅,可能是因为你没有规定最短更新时间,有的更新速度可能很快'skips'就过去了。为确保您看到每个进度条步骤,请尝试在更新进度条方法中的某处插入 Thread.Sleep(50)
。
你为什么不尝试继承 ProgressBar
class 并自己绘制东西?
假设您使用的是 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
中使用这个 class 而不是默认的 ProgressBar
class。
然后,你只需要将进度条的 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;
}
这将消除对任何其他功能的需求。
P.S。为什么你的函数是驼峰式的? C# 函数命名约定规定函数是 PascalCased(有人为此提供 link 吗?)。所以每个函数名的第一个字母都要大写。然而,这只是一个惯例。
我开发了一个名为 SxProgress 的 class 而不是 BackgroundWorker,它具有非常简单的界面,您可以按以下方式使用它:
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 ;
}
为了显示进度,class 创建了一个带有 ProgressBar 和标签的表单,并可选择处理 "Stop" 按钮来中断进程。
link SxProgress 概述和源代码:
另请注意,class 为并行处理提供了相同的简单接口,创建与计算机中的内核一样多的线程,并将项目透明地分派到不同的线程:
只需将 SxProgress.Execute()
更改为 SxProgress.ExecuteMulti()
。