Icons werden Falsch in TreeView geladen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von Matix Media.

    EaranMaleasi schrieb:

    So mal ganz nebenbei, normalerweise, sofern man mit Async/Await richtig umgeht, benötigt man keine Invoke()/BeginInvoke() aufrufe mehr.

    Man muss beachten, dass der Sync.-Context bei einem Task.Run() nicht gesetzt wird. Ein Beispiel:
    Wir haben eine Methode die lange braucht und dann und wann mal ein UI Update macht:

    C#-Quellcode

    1. private int nopValue = 0;
    2. private async Task LongRunningMethod()
    3. {
    4. while (true)
    5. {
    6. nopValue++;
    7. await Task.Delay(1000);
    8. Text = nopValue.ToString();
    9. }
    10. }

    Sie gibt einen Task zurück und ist awaitable (durch das async Keyword). Man könnte nun auf die Idee kommen und diese Methode mit Task.Run() zu starten: private Button_Click(object sender, EventArgs e) => Task.Run(LongRunningMethod); Nun wird man allerdings mit einer InvalidOperationException begrüßt. Macht auch Sinn. Es passiert Folgendes: Der EventHandler des Buttons läuft ganz regulär synchron im UI-Thread. Task.Run() startet einen neuen Thread, in welchem auf die UI zugegriffen wird => Rumms! Das kann nie klappen.

    Macht man es allerdings so: private async void button_Click(object sender, EventArgs e) => await LongRunningMethod(); funktioniert es wie geschmiert. Aber warum?
    Durch die async Deklaration des EventHandlers laufen await Aufrufe asynchron zur UI. Ein Task.Run() ist hier also total überflüssig. Jetzt aber zur Preisfrage
    Die LongRunningMethod läuft ja in beiden Fällen asynchron und greift auf die UI zu. Warum knallt es beim Ersten aber nicht beim Zweiten?

    Grundsätzlich müsste es auch im zweiten Fall knallen, aber das Control setzt mit WindowsFormsSynchronizationContext.InstallIfNeeded() den SynchronizationContext. Das gibt dem Thread die Möglichkeit den Code synchron zum UI Thread auszuführen. Ist eine Synchronisation dann Notwendig, passierts automatisch und Invoken kann man sich schenken.
    Es funktioniert also in etwa so:

    C#-Quellcode

    1. private void button_Click(object sender, EventArgs e)
    2. {
    3. Thread thread = new Thread(LongRunningMethod);
    4. thread.Start(SynchronizationContext.Current);
    5. }
    6. private int nopValue = 0;
    7. private void LongRunningMethod(object context)
    8. {
    9. while (true)
    10. {
    11. nopValue++;
    12. Thread.Sleep(1000);
    13. var syncContext = context as SynchronizationContext;
    14. if (syncContext != null)
    15. syncContext.Post(new SendOrPostCallback((o) => Text = nopValue.ToString()), null);
    16. }
    17. }

    Naja eigentlich nicht, aber so versteht man mal wie man mit Tasks überhaupt asynchron auf die UI zugreifen kann. Das ganze Zeugs mit dem SynchronizationContext läuft natürlich fein im Hintergrund. Deswegen ist async/await ja so bequem. Man muss nix synchronisieren oder Invoken, das wird alles für einen gemacht. :thumbup:
    Zusammenfassend:
    Wenn du also deinen EventHandler als async deklarierst wird dir direkt der passende Sync.Context gesetzt. Alle UI zugriffe können so synchronisiert werden. Deklariere eine Methode als async wenn du auf Code mit await warten willst.
    @Gonger96 Stimme dem meisten zu, nur das hier scheint mir nicht ganz richtig:

    Gonger96 schrieb:

    und ist awaitable (durch das async Keyword)
    Die wäre auch ohne async-Modifier awaitable. Das ist nicht der ausschlaggebende Punkt. Das async nutzt Du ja nur, weil Du innerhalb der Methode/Funktion awaitest.

    Sowas geht ja auch:

    C#-Quellcode

    1. ​public async void Handler(Object sender, EventArgs e) {
    2. await TestTask();
    3. }
    4. public Task TestTask() {
    5. return Task.Run(() => ... );
    6. }


    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!: