2010-11-18 23 views
2

J'utilise DispatcherTimer car je dois effectuer une opération toutes les deux minutes. A l'intérieur, j'appelle un BackgroundWorker pour faire mon travail, puis j'utilise le répartiteur attaché à la minuterie pour mettre à jour mon interface utilisateur. Je pense que l'erreur que je reçois a à voir avec le chronomètre, mais je ne suis pas sûr. Le répartiteur est-il fait ou l'arrière-plan? Comment puis-je faire le ReportProgress dans le foreach?L'opération BackgroundWorker ou (Dispatcher) est déjà terminée lors de l'exécution de ReportProgress

L'erreur:

This operation has already had OperationCompleted called on it and further calls are illegal.

lors de cette opération:

(sender as BackgroundWorker).ReportProgress((counterTotalSteps/100) * counterOnStep); 

Voici la procédure simplifiée:

DispatcherTimer dispTimer = new DispatcherTimer(); 
Dispatcher dispatcher = dispTimer.Dispatcher; 
dispTimer.Tick += delegate {dispTimer_Tick(dispatcher); }; 
dispTimer.Interval = new TimeSpan(0, 0, 45); 
dispTimer.Start(); 

private void DoWork(object sender,Dispatcher dispatcher) 
{ 
    int counterTotalSteps = PartialEmployees.Count(); 
    int counterOnStep = 1; 

    dispatcher.BeginInvoke(new Action(() => 
    {     
     AllEmployees.Clear(); 
     //calling the ReportProgress here works 
     foreach (var item in PartialEmployees) 
     { 
      counterOnStep ++; 
      //part below throws the error 
      (sender as BackgroundWorker).ReportProgress((counterTotalSteps/100) *  counterOnStep); 
      AllEmployees.Add(item);      
     } 
     counterOnStep = 0;    
    }));   
} 

EDIT: StackTrace:

at System.ComponentModel.AsyncOperation.VerifyNotCompleted() 
    at System.ComponentModel.AsyncOperation.Post(SendOrPostCallback d, Object arg) 
    at System.ComponentModel.BackgroundWorker.ReportProgress(Int32 percentProgress, Object userState) 
    at System.ComponentModel.BackgroundWorker.ReportProgress(Int32 percentProgress) 
    at testDispatcher.ViewModel.EmployeeListViewModel.<>c__DisplayClass7.<DoWork>b__6() in C:\Users\kozaj\Documents\Visual Studio 2010\Projects\testDispatcher\testDispatcher\ViewModel\EmployeeListViewModel.cs:line 91 
    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) 
    at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) 
    at System.Windows.Threading.DispatcherOperation.InvokeImpl() 
    at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state) 
    at System.Threading.ExecutionContext.runTryCode(Object userData) 
    at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData) 
    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Windows.Threading.DispatcherOperation.Invoke() 
    at System.Windows.Threading.Dispatcher.ProcessQueue() 
    at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) 
    at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) 
    at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) 
    at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) 
    at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler) 
    at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) 
    at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam) 
    at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg) 
    at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame) 
    at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame) 
    at System.Windows.Threading.Dispatcher.Run() 
    at System.Windows.Application.RunDispatcher(Object ignore) 
    at System.Windows.Application.RunInternal(Window window) 
    at System.Windows.Application.Run(Window window) 
    at System.Windows.Application.Run() 
    at testDispatcher.App.Main() in C:\Users\kozaj\Documents\Visual Studio 2010\Projects\testDispatcher\testDispatcher\obj\x86\Debug\App.g.cs:line 50 
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 
+0

Quelle est la trace de la pile? – SLaks

+1

Pourquoi utilisez-vous un BackgroundWorker, mais effectuez tout le travail sur le thread d'interface utilisateur? – SLaks

+0

Ceci est juste un exemple de code, pour voir si je peux le faire fonctionner correctement avant de l'implémenter dans d'autres choses. Quelle partie bougeriez-vous en dehors du dipatcher? Ces listes sont également liées à l'interface utilisateur. – LobalOrning

Répondre

3

Séquence d'événements

  1. DoWork est appelé
  2. DoWork met "AllEmployees.Clear();" dans la file d'attente du répartiteur
  3. DoWork termine
  4. Le répartiteur voit "AllEmployees.Clear();" et commence à traiter cette fonction.

Je suggère d'utiliser dispatcher.Invoke (qui l'exécute immédiatement) uniquement sur les étapes qui ont réellement une interaction UI.

+0

L'utilisation de .Invoke() a bien fonctionné. Ce que je fais est l'incrémentation d'un ProgressBar, mais il semble faire tout à la fois (directement à 100%). Quand je mets un point d'arrêt sur WorkerProgressChanged(), il n'est pas appelé juste quand le foreach l'appelle, il est plutôt appelé après le foreach pour combien de fois il a été appelé - ou il ressemble à ça. Aucune suggestion? – LobalOrning

+0

'ReportProgress' appelle en interne' BeginInvoke' pour signaler la progression. Le message qu'il publie n'est traité que lorsque le thread de l'interface utilisateur est libre, une fois que vous avez terminé. – SLaks

1

Étant donné que votre travail est effectué sur le thread de l'interface utilisateur, vous ne devez pas utiliser de BackgroundWorker. Au lieu de cela, vous devez mettre à jour la barre de progression directement à l'intérieur de la boucle.

+0

Lorsque vous dites que mon travail est en cours sur le thread de l'interface utilisateur, dites-vous que parce que dispTimer_Tick() se trouve sur le thread UI et quand il crée BackgroundWorker, il est également sur le thread UI? – LobalOrning

+0

@Lobal: BackgroundWorker s'exécutera sur un thread d'arrière-plan. Cependant, que pensez-vous de 'dispatcher.BeginInvoke'? – SLaks

+0

Place l'action dans la file d'attente de threads de l'interface utilisateur. J'ai besoin de faire la boucle parce que ces listes sont liées à l'interface utilisateur, je ne sais pas comment je pourrais "actualiser" ces listes. Désolé si mon travail est bâclé, je suis un tout nouveau codeur. – LobalOrning