Comment implémenter une barre de progression et un arrière-plan pour les appels de base de données en C #?
J'ai des méthodes qui traitent de grandes quantités de données. Ce sont des opérations relativement longues, je souhaite donc implémenter une barre de progression pour informer l'utilisateur que quelque chose se passe réellement.
J'ai pensé utiliser une étiquette de barre de progression ou de barre de progression, mais comme il existe un seul thread d'interface utilisateur, le fil sur lequel les méthodes de traitement de la base de données sont exécutées, les contrôles de l'interface utilisateur ne sont pas mis à jour.
J'ai déjà vu quelques exemples, mais ils traitent de boucles for-loop, ex:
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);
}
Je cherche de meilleurs exemples.
Certaines personnes peuvent ne pas aimer, mais voici ce que je fais:
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;
}
Le style Marquee nécessite l'activation de VisualStyles, mais il défile continuellement sans qu'il soit nécessaire de le mettre à jour. J'utilise cela pour les opérations de base de données qui ne rendent pas compte de leurs progrès.
Si vous ne pouvez pas connaître la progression, vous ne devriez pas la simuler en abusant d'une barre de progression, mais affichez simplement une sorte d'icône occupée, telle que en.wikipedia.org/wiki/Throbber#Spinning_wheel. fini. Cela ferait une interface graphique plus "honnête".
Lorsque vous effectuez des opérations sur le fil d'arrière-plan et que vous souhaitez mettre à jour l'interface utilisateur, vous ne pouvez rien appeler ni définir depuis le fil d'arrière-plan. Dans le cas de WPF, vous avez besoin de Dispatcher.BeginInvoke et, dans le cas de WinForms, de la méthode 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);
});
}
pour WinForms, le délégué peut nécessiter un casting ou vous pouvez avoir besoin de peu d’aide, ne vous souvenez plus de la syntaxe exacte maintenant.
L'idée derrière le signalement des progrès avec le travailleur d'arrière-plan consiste à envoyer un événement "Pourcentage terminé". Vous êtes vous-même responsable de déterminer «combien» de travail a été accompli. Malheureusement, c'est souvent la partie la plus difficile.
Dans votre cas, le gros du travail est lié à la base de données. À ma connaissance, il n’ya aucun moyen d’obtenir directement des informations sur les progrès de la base de données. Ce que vous pouvez essayer de faire cependant, c'est fractionner le travail de manière dynamique. Par exemple, si vous avez besoin de lire beaucoup de données, une méthode naïve pour implémenter cela pourrait être.
Divisez la lecture réelle en morceaux plus petits, en indiquant les progrès accomplis à chaque fois qu'un morceau est terminé:
for (int i = 0; i < count; i++)
{
bgWorker.ReportProgress((100 * i) / count);
// ... (read data for step i)
}
Je n'ai pas compilé ceci car il est destiné à une validation de principe. Voici comment j'ai implémenté une barre de progression pour l'accès à la base de données dans le passé. Cet exemple montre l'accès à une base de données SQLite à l'aide du module 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);
}
Cela servira. Facile à mettre en œuvre, testé à 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
}