Как реализовать индикатор выполнения на С#?
Как реализовать панель выполнения и фонового рабочего для вызовов базы данных в С#?
У меня есть несколько методов, которые обрабатывают большие объемы данных. Это относительно длительные операции, поэтому я хочу реализовать индикатор выполнения, чтобы пользователь знал, что что-то происходит на самом деле.
Я думал о том, чтобы использовать индикатор выполнения или метку полосы состояния, но поскольку существует один поток пользовательского интерфейса, поток, в котором выполняются методы обработки базы данных, элементы управления пользовательского интерфейса не обновляются, что делает индикатор выполнения или метку полосы состояния бесполезными мне.
Я уже видел некоторые примеры, но они имеют дело с for-loops, например:
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);
}
Я ищу лучшие примеры.
Ответы
Ответ 1
Некоторым людям это может не нравиться, но это то, что я делаю:
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 включался, но он непрерывно прокручивается самостоятельно, без необходимости обновления. Я использую это для операций с базой данных, которые не сообщают о своем прогрессе.
Ответ 2
Если вы не можете знать прогресс, вы не должны его подделывать, злоупотребляя индикатором выполнения, вместо этого просто показывайте какой-то значок занятости, например en.wikipedia.org/wiki/Throbber#Spinning_wheel. Покажите его при запуске задачи и скройте когда он закончил. Это сделало бы для более "честного" GUI.
Ответ 3
Когда вы выполняете операции над фоновым потоком и хотите обновить интерфейс, вы не можете вызывать или устанавливать что-либо из фонового потока. В случае 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 может потребоваться некоторое литье, или вам может понадобиться небольшая помощь, не помните точный синтаксис.
Ответ 4
Идея достижения прогресса в работе с фоновым работником заключается в отправке события с "процентом завершенности". Вы сами несете ответственность за определение как-то "сколько" работы было завершено. К сожалению, это часто самая сложная часть.
В вашем случае основная часть работы связана с базой данных. Насколько я знаю, нет возможности напрямую получать информацию о ходе работы с БД. Однако то, что вы можете попытаться сделать, - это динамическое разделение работы. Например, если вам нужно прочитать много данных, наивный способ реализовать это может быть.
- Определите, сколько строк нужно извлечь (SELECT COUNT (*) FROM...)
-
Разделите фактическое чтение в небольших кусках, сообщив о ходе выполнения каждого раза:
for (int i = 0; i < count; i++)
{
bgWorker.ReportProgress((100 * i) / count);
// ... (read data for step i)
}
Ответ 5
Я не компилировал это, поскольку он предназначен для доказательства концепции. Вот как я в прошлом реализовал панель выполнения для доступа к базе данных. В этом примере показан доступ к базе данных SQLite с использованием модуля System.Data.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);
}
Ответ 6
Это будет полезно. Легко реализовать, 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
}