[WPF] Abfangen wenn Thread fertig ist

  • C#

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von thefiloe.

    [WPF] Abfangen wenn Thread fertig ist

    Hey,

    ich möchte einen Thread starten und abfangen, wenn der Thread fertig ist / seine Arbeit erledigt hat.
    Das Ganze hängt mit einem ICommand zusammen, der dann das CanExecuteChanged-Event aufrufen soll.

    Mein Lösungsansatz ist folgender:

    C#-Quellcode

    1. Thread t = new Thread(new ThreadStart(this._OnRun));


    C#-Quellcode

    1. private void _OnRun()
    2. {
    3. this.OnRun(); // Methode nach der CanExecute geupdated werden soll
    4. this.RunCommand.RaiseCanExecuteChanged();
    5. }


    Mein RunCommand ist ein normaler Command aus dem MVVM-Pattern.
    Spoiler anzeigen

    C#-Quellcode

    1. public class ActionCommand : ICommand
    2. {
    3. private readonly Action<object> _executeHandler;
    4. private readonly Func<object, bool> _canExecuteHandler;
    5. public ActionCommand(Action<object> execute, Func<object, bool> canExecute)
    6. {
    7. if (execute == null)
    8. {
    9. throw new ArgumentNullException("execute cannot be null");
    10. }
    11. _executeHandler = execute;
    12. _canExecuteHandler = canExecute;
    13. }
    14. public event EventHandler CanExecuteChanged;
    15. public void Execute(object parameter)
    16. {
    17. _executeHandler(parameter);
    18. }
    19. public bool CanExecute(object parameter)
    20. {
    21. if (_canExecuteHandler == null)
    22. {
    23. return true;
    24. }
    25. return _canExecuteHandler(parameter);
    26. }
    27. public void RaiseCanExecuteChanged()
    28. {
    29. var handler = CanExecuteChanged;
    30. if (handler != null)
    31. {
    32. handler.Invoke(this, EventArgs.Empty);
    33. }
    34. }
    35. }



    Allerdings greife ich dann von einen Thread auf einen anderen zu und deshalb tritt folgender Fehler auf:

    Quellcode

    1. Eine nicht behandelte Ausnahme des Typs "System.InvalidOperationException" ist in WindowsBase.dll aufgetreten.
    2. Zusätzliche Informationen: Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da sich das Objekt im Besitz eines anderen Threads befindet.



    Habe es auch schon so versucht, funktioniert aber auch nicht. Wende ich lock falsch an?

    C#-Quellcode

    1. lock (this.RunCommand)
    2. {
    3. this.RunCommand.RaiseCanExecuteChanged();
    4. }


    Es wäre sehr nett, wenn ihr mir hier weiterhelfen könntet. :)

    Viele Grüße,
    Trudi
    Der fertige Thread könnte doch ein Event raisen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi, danke für die Antwort.
    Das funktioniert leider auch nicht, wenn ich das Event feuer kommt wieder der selbe Fehler.
    Habe es so umgesetzt (Alles in der selben Klasse):

    C#-Quellcode

    1. private event EventHandler RunningThreadEnded;

    C#-Quellcode

    1. private void _OnRun()
    2. {
    3. this.OnRun();
    4. RunningThreadEnded(this, EventArgs.Empty);
    5. }

    C#-Quellcode

    1. void BasicPlugin_RunningThreadEnded(object sender, EventArgs e)
    2. {
    3. this.RunCommand.RaiseCanExecuteChanged();
    4. }



    Der Fehler tritt übrigens immer im ICommand bei folgendem Aufruf auf:

    C#-Quellcode

    1. handler.Invoke(this, EventArgs.Empty);


    MfG
    Hey,
    habe es jetzt selbst gebacken bekommen.
    Ich habe mir einfach den Dispatcher vom Haupt-Thread zwischengespeichert und ich greife dann in dem neuen Thread auf den zurück, um den neuen Thread nach dem beenden zurückzusetzen. (Verwirrend nich?)

    C#-Quellcode

    1. this.dispatcher = Dispatcher.CurrentDispatcher;

    C#-Quellcode

    1. private void _OnRun()
    2. {
    3. this.OnRun();
    4. dispatcher.Invoke(new Action(this.ResetRunningThread), null);
    5. }

    C#-Quellcode

    1. private void ResetRunningThread()
    2. {
    3. this.RunningThread = null;
    4. this.RunCommand.RaiseCanExecuteChanged();
    5. }


    MfG Trudi
    Obwohl das Thema schon erledigt ist, gibt es einen kleinen Trick wodurch der RaiseCanExecuteChanged() aufruf hinfällig wird.
    Du kannst in deinem Command folgendes hinzufügen:

    Quellcode

    1. event EventHandler ICommand.CanExecuteChanged
    2. {
    3. add { CommandManager.RequerySuggested += value; }
    4. remove { CommandManager.RequerySuggested -= value; }
    5. }


    Dadurch sollte in 99% der Fälle der Aufruf RaiseCanExecuteChanged hinfällig werden.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Und wie rufe ich dann den EventHandler auf?
    Hatte bei meinem Command (aus nem WPF-Buch kopiert) genau das schon eingebaut, aber dann zu public event EventHandler CanExecuteChanged; geändert, damit ich das Event auch aus RaiseCanExecuteChanged aufrufen kann.
    Wie "invoke" ich das Event denn dann außerhalb vom Command?

    Bei mir kommt dann folgender Fehler:

    Quellcode

    1. Das Ereignis "ActionCommand.CanExecuteChanged" kann nur links von += oder -= verwendet werden.




    Würde mich interessieren, weil mein Buch schreibt mir das auch so vor...

    MfG

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Trudi“ ()

    Das sind meine zwei Commands. Der erste ist die Basis und funktioniert so wie deiner. Der zweite kann das Selbe wie der Erste nur eben, dass es auch automatisch aktualisiert:
    Spoiler anzeigen

    Quellcode

    1. public class DelegateCommand : ICommand
    2. {
    3. private readonly Predicate<object> _canExecute;
    4. private readonly Action<object> _execute;
    5. public event EventHandler CanExecuteChanged;
    6. public DelegateCommand(Action<object> execute)
    7. : this(execute, null)
    8. {
    9. }
    10. public DelegateCommand(Action<object> execute,
    11. Predicate<object> canExecute)
    12. {
    13. _execute = execute;
    14. _canExecute = canExecute;
    15. }
    16. public virtual bool CanExecute(object parameter)
    17. {
    18. if (_canExecute == null)
    19. {
    20. return true;
    21. }
    22. return _canExecute(parameter);
    23. }
    24. public virtual void Execute(object parameter)
    25. {
    26. _execute(parameter);
    27. }
    28. public virtual void RaiseCanExecuteChanged()
    29. {
    30. if (CanExecuteChanged != null)
    31. {
    32. CanExecuteChanged(this, EventArgs.Empty);
    33. }
    34. }
    35. }

    Quellcode

    1. public class AutoDelegateCommand : DelegateCommand, ICommand
    2. {
    3. public AutoDelegateCommand(Action<object> execute)
    4. : base(execute)
    5. {
    6. }
    7. public AutoDelegateCommand(Action<object> execute, Predicate<object> canExecute)
    8. : base(execute, canExecute)
    9. {
    10. }
    11. event EventHandler ICommand.CanExecuteChanged
    12. {
    13. add { CommandManager.RequerySuggested += value; }
    14. remove { CommandManager.RequerySuggested -= value; }
    15. }
    16. }


    Du hast einmal das Ereignis CanExecuteChanged implicit implementiert und einmal(für die automatische Version) explizit implementiert(siehe das ICommand.CanExecuteChanged).


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Ja, habe ich. Ich glaube ich verstehe auch die Logik dahinter.

    Das ist die Änderung, bei der mein Button wieder erscheinen soll:

    C#-Quellcode

    1. this.RunningThread = null;


    Und so kommt das in meinem Command vor (Zeile 3):

    C#-Quellcode

    1. public bool OnRunCanExecute(object parameter = null)
    2. {
    3. if (this.DontRunMultiThreaded == true || this.RunningThread == null || this.RunningThread.IsAlive == false)
    4. {
    5. return true;
    6. }
    7. return false;
    8. }


    Zur Sicherheit poste ich auch nochmal meinen Basis-Command und die Vererbung:

    Basis

    C#-Quellcode

    1. public class ActionCommand : ICommand
    2. {
    3. private readonly Action<object> _executeHandler;
    4. private readonly Func<object, bool> _canExecuteHandler;
    5. public ActionCommand(Action<object> execute, Func<object, bool> canExecute)
    6. {
    7. if (execute == null)
    8. {
    9. throw new ArgumentNullException("execute cannot be null");
    10. }
    11. _executeHandler = execute;
    12. _canExecuteHandler = canExecute;
    13. }
    14. public event EventHandler CanExecuteChanged;
    15. public void Execute(object parameter)
    16. {
    17. _executeHandler(parameter);
    18. }
    19. public bool CanExecute(object parameter)
    20. {
    21. if (_canExecuteHandler == null)
    22. {
    23. return true;
    24. }
    25. return _canExecuteHandler(parameter);
    26. }
    27. public void RaiseCanExecuteChanged()
    28. {
    29. var handler = CanExecuteChanged;
    30. if (handler != null)
    31. {
    32. handler.Invoke(this, EventArgs.Empty);
    33. }
    34. }

    AutoCommand

    C#-Quellcode

    1. public class AutoActionCommand : ActionCommand, ICommand
    2. {
    3. public AutoActionCommand(Action<object> execute, Func<object, bool> canExecute)
    4. : base(execute, canExecute)
    5. {
    6. }
    7. event EventHandler ICommand.CanExecuteChanged
    8. {
    9. add { CommandManager.RequerySuggested += value; }
    10. remove { CommandManager.RequerySuggested -= value; }
    11. }
    12. }


    Wie soll die CLR denn da erkennen ob sich was geändert hat? :)

    MfG
    Hmm scheint richtig zu sein. Funktionieren tut dies über den CommandManager. Genaueres dazu kannst du dir auf msdn durchlesen.
    Außerdem kannst du trotzdem deinen den Code reduzieren.
    Dein CanExecute kommt mit genau einer Zeile aus:

    Quellcode

    1. return this.DontRunMultiThreaded || this.RunningThread == null || !this.RunningThread.IsAlive


    Leider scheint der Command hier wirklich nicht ganz zu funktionieren. Für normal funktioniert dies für fast alles(behalt es also im Hinterkopf). Du kannst es auch einmal bei z.b. einem Menu ausprobieren. Hatte dies grad erst gestern gemacht und da funktioniert es zu 100%.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.