2017-09-29 7 views
1

J'ai une application avec un GUI et une zone de texte enrichi où je publie ce que le programme est en train de faire car le traitement des données peut être assez long.Processus de mise à jour de la méthode appelée par le travailleur de fond C#

J'ai essayé deux approches pour que:

1 Dans la BackgroundWorker méthode que je peux appeler le code suivant fin:

GlobalVar.backgroundWorkerAppendText = task.Build_CSV_List(); 
Processchange(); 

Alors que je ne peux pas utiliser Form1.Processchange(); dans la classe d'aide en raison de la non statique contexte

2 J'ai donc essayé de créer mon tout premier Eventhandler.
L'idée était que helper.UpdateConsole() soulèverait un événement

public event EventHandler OnConsoleUpdate; 
public void Consoleupdate() 
{ 
    OnConsoleUpdate(this, EventArgs.Empty); 
} 

auquel le BackgroundWorker écoute et appelle ensuite Processchange de son contexte

public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    StandardTasks task = new StandardTasks(); 
    Helper helper = new Helper(); 
    helper.OnConsoleUpdate += Processchange; 
    task.DoSomeStuffHere() 
} 
public void Processchange(object sender=null, EventArgs e=null) 
{ 
    //MessageBox.Show(GlobalVar.backgroundWorkerAppendText); 
    GlobalVar.next = false; 
    backgroundWorker1.ReportProgress(1); 
    while (GlobalVar.next == false) 
    { 
     helper.TimeBreaker(100,"ms"); 
    } 
} 

Malheureusement, ce fut sans succès. Dès que je monte l'événement, je reçois l'erreur System.NullReferenceException qui, après avoir fait un googling, m'amène à la conclusion qu'il n'y a pas de listerner attaché à l'événement eventhouh que je l'ai joint dans le Backgroundworker Do work.

Edit: le OnConsoleUpdate() == null comme indiqué sur la capture d'écran ci-dessous

event = null

L'aide est dans un autre fichier de classe "aides" qui pourrait être important pour une solution.

J'espère que vous pouvez m'aider.

+0

est-il une option pour passer à motif Async base de travail (ROBINET)? Ensuite, vous pouvez simplement démarrer une tâche sur le ThreadPool et lui passer un objet Progress. Si vous voulez coller avec l'arrière-plan, vous devez afficher plus de code pertinent. – Fildor

+0

Où avez-vous démarré cet arrière-plan? Je vois quelque chose comme ça: backgroundWorker1.RunWorkerAsync(); – Arkadiusz

+0

BTW: Avoir une classe "GlobalVar" ... qui a une odeur de code ... – Fildor

Répondre

2

Bienvenue à SO!

Quelques petites choses me viennent immédiatement à l'esprit.

Tout d'abord, éliminons le problème de l'événement. Vous avez la bonne approche - vous avez besoin d'un événement et d'une méthode pour l'appeler, mais cette méthode doit vérifier si l'événement est null.

En gros, faites ceci:

public event EventHandler OnConsoleUpdate; 
public void ConsoleUpdate() 
{ 
    OnConsoleUpdate?.Invoke(this, EventArgs.Empty); 
} 

Le fait ci-dessus l'utilisation de ?, un opérateur état nul. Vous pouvez en savoir plus à ce sujet on this MSDN page.

Deuxième chose ... on ne sait pas vraiment ce que votre travailleur d'arrière-plan est réellement. On dirait que c'est une sorte de cours personnalisé que vous avez créé? La raison pour laquelle c'est important est que .NET a en fait une classe BackgroundWorker utilisée pour les opérations en cours ... bien, en arrière-plan. Il a également un événement OnProgressChanged auquel vous pouvez vous connecter et qui peut être utilisé pour mettre à jour l'interface utilisateur (n'oubliez pas de définir la propriété WorkerReportsProgress sur true). Et pour utiliser le BackgroundWorker mentionné ci-dessus, vous ne devriez pas avoir besoin de créer vos propres événements.

Voici comment vous pouvez utiliser la norme.NET BackgroundWorker:

System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker(); 

void StartBackgroundTask() 
{ 
    worker.DoWork += worker_DoWork; 
    //if it's possible to display progress, use this 
    worker.WorkerReportsProgress = true; 
    worker.ProgressChanged += worker_ProgressChanged; 
    //what to do when the method finishes? 
    worker.RunWorkerCompleted += worker_RunWorkerCompleted; 
    //start! 
    worker.RunWorkerAsync(); 
} 

void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
{ 
    //perform any "finalization" operations, like re-enable disabled buttons 
    //display the result using the data in e.Result 
    //this code will be running in the UI thread 
} 

//example of a container class to pass more data in the ReportProgress event 
public class ProgressData 
{ 
    public string OperationDescription { get; set; } 
    public int CurrentResult { get; set; } 
    //feel free to add more stuff here 
} 

void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) 
{ 
    //display the progress using e.ProgressPercentage or e.UserState 
    //this code will be running in the UI thread 
    //UserState can be ANYTHING: 
    //var data = (ProgressData)e.UserState; 
} 

void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
{ 
    //this code will NOT be running in the UI thread! 
    //you should NOT call the UI thread from this method 

    int result = 1; 
    //perform calculations 
    for (var i = 1; i <= 10; i++) 
    { 
     worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result }); 
     System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); 
     result *= i; 
    } 

    e.Result = result; 
} 

Maintenant, la chose au sujet de la classe BackgroundWorker est qu'il est assez vieux, et avec les versions .NET actuelles, vous pouvez utiliser les async/await mots-clés pour gérer facilement les opérations d'arrière-plan et les mises à jour l'interface utilisateur, mais cela est probablement aller en dehors des limites de cette question. Cela dit, l'existence de async/await n'invalide pas l'utilisation de BackgroundWorker ce qui est assez simple dans son utilisation.

Il y a une chose plus inquiétante dans votre code.

public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    StandardTasks task = new StandardTasks(); //<- you create a task 
    Helper helper = new Helper(); // <- you create a helper 
    helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper event 
    task.DoSomeStuffHere(); // <- you do stuff with the task... but the task doesn't know about your helper above! Does `StandardTasks` use `Helper`? If so, how? 
} 

Prenez note que les événements, à moins d'être static, ne sont pas globaux. Ainsi, se connecter à un événement dans une instance d'une classe ne provoquera pas une autre instance de cette classe à "déclencher" cet événement. Il semble un moyen de résoudre vos problèmes serait de faire de la prendre classe StandardTasksHelper comme l'un des paramètres du constructeur, de sorte que le code ressemblerait à ceci:

Helper helper = new Helper(); // <- you create a helper 
helper.OnConsoleUpdate += Processchange; // <- you hook up to the helper class event to actually do something 
StandardTasks task = new StandardTasks(helper); //<- you create a task which will use the helper with the hooked up event above 
+0

La vérification du gestionnaire d'événement pour une valeur nulle comme ceci n'est pas sûre et peut toujours entraîner une exception, car un gestionnaire ajouté peut être supprimé entre la vérification de null et l'appel du gestionnaire. Je recommande d'utiliser l'opérateur conditionnel null (?.) Ou en enregistrant OnConsoleUpdate à une variable locale et en utilisant cela pour la vérification et l'invocation de null. –

+1

@ SzabolcsDézsi Vous avez absolument raison. Je vais éditer la réponse. Quoique, pour être juste, c'était la manière standard de gérer les événements avant le? opérateur a été ajouté je crois ... – Shaamaan

+0

Bonjour Shaamaan, merci pour votre aide. Votre code est en fait ce que j'ai dans mon code, mais il ne fonctionne pas. pour le backgroubndworker je l'ai "tiré" de la fenêtre des outils dans le concepteur de formulaire. Je peux aussi faire un Processchanged mais seulement directement depuis l'arrière-plan. pas d'une méthode qu'il a appelée. –