[英]How do I implement a progress bar in C#?
C#如何实现数据库调用的进度条和backgroundworker?
我确实有一些处理大量数据的方法。 它们是运行时间相对较长的操作,所以我想实现一个进度条让用户知道某些事情正在发生。
我想到了使用进度条或状态条 label,但由于只有一个 UI 线程,执行数据库处理方法的线程,UI 控件不更新,使得进度条或状态条 label 对我来说毫无用处。
我已经看过一些示例,但它们处理的是 for 循环,例如:
for(int i = 0; i < count; i++)
{
System.Threading.Thread.Sleep(70);
// ... do analysis ...
bgWorker.ReportProgress((100 * i) / count);
}
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = Math.Min(e.ProgressPercentage, 100);
}
我正在寻找更好的例子。
有些人可能不喜欢它,但这就是我所做的:
private void StartBackgroundWork() {
if (Application.RenderWithVisualStyles)
progressBar.Style = ProgressBarStyle.Marquee;
else {
progressBar.Style = ProgressBarStyle.Continuous;
progressBar.Maximum = 100;
progressBar.Value = 0;
timer.Enabled = true;
}
backgroundWorker.RunWorkerAsync();
}
private void timer_Tick(object sender, EventArgs e) {
if (progressBar.Value < progressBar.Maximum)
progressBar.Increment(5);
else
progressBar.Value = progressBar.Minimum;
}
Marquee样式需要启用VisualStyles,但它会不断滚动,无需更新。 我将其用于不报告其进度的数据库操作。
如果你不知道进展你不应该通过滥用进度条来伪造它,而只是显示某种繁忙的图标,如en.wikipedia.org/wiki/Throbber#Spinning_wheel在启动任务时显示它并在它的时候隐藏它完了。 这将使一个更“诚实”的GUI。
当您在后台线程上执行操作并且想要更新UI时,您无法从后台线程调用或设置任何内容。 对于WPF,您需要Dispatcher.BeginInvoke,如果是WinForms,则需要Invoke方法。
WPF:
// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
DoSomething();
this.Dispatcher.BeginInvoke((Action)delegate(){
this.progressBar.Value = (int)((100*i)/count);
});
}
的WinForms:
// assuming "this" is the window containing your progress bar..
// following code runs in background worker thread...
for(int i=0;i<count;i++)
{
DoSomething();
this.Invoke(delegate(){
this.progressBar.Value = (int)((100*i)/count);
});
}
对于WinForms委托可能需要一些演员或你可能需要一点帮助,不记得现在的确切语法。
向后台工作人员报告进度的想法是通过发送'完成百分比'事件。 你自己负责确定已经完成了多少“工作量”。 不幸的是,这通常是最困难的部分。
在您的情况下,大部分工作与数据库相关。 据我所知,无法直接从DB获取进度信息。 然而,你可以尝试做的是动态地分解工作。 例如,如果您需要阅读大量数据,可以采用一种天真的方式来实现这一点。
将实际读数除以较小的块,每次完成一个块时报告进度:
for (int i = 0; i < count; i++) { bgWorker.ReportProgress((100 * i) / count); // ... (read data for step i) }
我没有编译它,因为它是用于概念证明。 这就是我过去为数据库访问实现进度条的方法。 此示例显示使用System.Data.SQLite模块访问SQLite数据库
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Get the BackgroundWorker that raised this event.
BackgroundWorker worker = sender as BackgroundWorker;
using(SQLiteConnection cnn = new SQLiteConnection("Data Source=MyDatabase.db"))
{
cnn.Open();
int TotalQuerySize = GetQueryCount("Query", cnn); // This needs to be implemented and is not shown in example
using (SQLiteCommand cmd = cnn.CreateCommand())
{
cmd.CommandText = "Query is here";
using(SQLiteDataReader reader = cmd.ExecuteReader())
{
int i = 0;
while(reader.Read())
{
// Access the database data using the reader[]. Each .Read() provides the next Row
if(worker.WorkerReportsProgress) worker.ReportProgress(++i * 100/ TotalQuerySize);
}
}
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Notify someone that the database access is finished. Do stuff to clean up if needed
// This could be a good time to hide, clear or do somthign to the progress bar
}
public void AcessMySQLiteDatabase()
{
BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
}
这将有助于实现,100%经过测试。
for(int i=1;i<linecount;i++)
{
progressBar1.Value = i * progressBar1.Maximum / linecount; //show process bar counts
LabelTotal.Text = i.ToString() + " of " + linecount; //show number of count in lable
int presentage = (i * 100) / linecount;
LabelPresentage.Text = presentage.ToString() + " %"; //show precentage in lable
Application.DoEvents(); keep form active in every loop
}
您必须从线程执行流程,并从线程调用进度条并更改其值,也许这个示例可以帮助您
public void main()
{
int count = 20;
progressbar.Maximum = count;
progressbar.Value = 0;
new Thread(() => Work(progressbar, count)).Start();
}
public static void Work(ProgressBar progressbar, int count)
{
for (int i = 0; i <= count; i++)
{
Thread.Sleep(70);
// ... do analysis ...
Application.Current.Dispatcher.Invoke(new Action(() =>
{
progressbar.Value = i;
}));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.