PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : WPF-Storyboards


Mr. Lolman
2018-11-29, 10:55:48
Hab ein Problem mit einem Customcontrol:


Storyboard mit DoubleAnimation über Klick-EventTrigger in XAML


Das Problem ist folgendes: Die Klickanimation startet mit PreviewMouseLeftButtonDown und die Anwendung reagiert aufs Klickevent (also ButtonUp) und blockiert dabei den UI-Thread. Die UI-Thread Blockade lässt sich nicht verhindern, jedoch soll die Animation auf jeden Fall durchlaufen. D.h. die Anwendung darf frühestens nach Ablauf der Animation auf den Klick reagieren, aber nur dann wenn der Button zu dem Zeitpunkt nicht mehr gedrückt ist.

Wie mach ich das am Besten?

FlashBFE
2018-11-29, 14:08:54
Die UI-Thread Blockade lässt sich nicht verhindern
Genau das ist falsch. Natürlich lässt sich das verhindern und das sollte man auch tun. Das einfachste wäre das Async/Await-Prinzip:

VB (https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/concepts/async/)/C# (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)

][immy
2018-11-29, 14:55:21
Hab folgendes Problem mit einem Customcontrol:


Storyboard mit DoubleAnimation über Klick-EventTrigger in XAML


Das Problem ist folgendes: Die Klickanimation startet mit PreviewMouseLeftButtonDown und die Anwendung reagiert aufs Klickevent (also ButtonUp) und blockiert dabei den UI-Thread. Die UI-Thread Blockade lässt sich nicht verhindern, jedoch soll die Animation auf jeden Fall durchlaufen. D.h. die Anwendung darf frühestens nach Ablauf der Animation auf den Klick reagieren, aber nur dann wenn der Button zu dem Zeitpunkt nicht mehr gedrückt ist.

Wie mach ich das am Besten?
Also da gäbe es async/await oder etwas zuverlässiger (meiner Erfahrung nach) eigene Threads zu verwenden um vom Klick-Event direkt einen Thread zu starten.
Sobald man wieder mit der Oberfläche interagieren will wieder mit dem UI-Thread joinen … allerdings hab ich nicht so viel Erfahrung mit WPF aber in WinForms läuft es ja grundsätzlich genau so.

Etwas einfacheres, einfach die Oberfläche sperren solange eine Animation läuft, dann ist der Klick allerdings verloren.

Monger
2018-11-29, 15:11:28
Beides geht auf jeden Fall nicht. Der Event Handler wird synchron zum Haupt UI Thread abgearbeitet. Ergo, du kannst nicht im Event sitzen und gleichzeitig die UI weiterlaufen lassen. Eine Alternative wäre ein zweiter UI Thread, aber da begibst du dich auf gefährliches Territorium. Das sauber zu machen ist Hölle.

Sauberste Lösung mMn:
Im Button Event auf das Ende der Animation registrieren, d.h. der feuert ein neues UI Event. Animation starten.
Im zweiten UI Event dann deine Ablauflogik machen. Wenn das irgendwas langläufiges ist, am besten asynchron als Backgroundworker starten, oder halt n bissl moderner: als async Task. Je nachdem was das ist, wirst du auf den nicht mehr warten müssen.

Mr. Lolman
2018-11-30, 07:42:01
Genau das ist falsch. Natürlich lässt sich das verhindern und das sollte man auch tun. Das einfachste wäre das Async/Await-Prinzip:

VB (https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/concepts/async/)/C# (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)

[immy;11864289']Also da gäbe es async/await oder etwas zuverlässiger (meiner Erfahrung nach) eigene Threads zu verwenden um vom Klick-Event direkt einen Thread zu starten.
Sobald man wieder mit der Oberfläche interagieren will wieder mit dem UI-Thread joinen … allerdings hab ich nicht so viel Erfahrung mit WPF aber in WinForms läuft es ja grundsätzlich genau so.

Etwas einfacheres, einfach die Oberfläche sperren solange eine Animation läuft, dann ist der Klick allerdings verloren.


Tja, leider geht das wirklich nicht, da das Customcontrol in einer (nicht .NET) Win32 Anwendung eingebunden ist, die in einer eher exotischen Programmiersprache entwickelt wurde. Sobald die Anwendung den Klick mitbekommt, wird die UI blockiert. Und das liegt natürlich nicht an WPF selbst, sondern an der Anwendung. Mit nativen Buttons würde die UI genauso blockiert, nur störts da nicht, weil alles statisch ist.


Ich werds wohl echt, wie von Monger vorgeschlagen, mit einem neuen UI-Event lösen müssen. (Wobei ein 2. UI-Thread schon irgendwie sexy wär)

xxxgamerxxx
2018-12-05, 18:13:18
Tja, leider geht das wirklich nicht, da das Customcontrol in einer (nicht .NET) Win32 Anwendung eingebunden ist, die in einer eher exotischen Programmiersprache entwickelt wurde. Sobald die Anwendung den Klick mitbekommt, wird die UI blockiert. Und das liegt natürlich nicht an WPF selbst, sondern an der Anwendung. Mit nativen Buttons würde die UI genauso blockiert, nur störts da nicht, weil alles statisch ist.


Dann hast du es vermutlich nicht richtig gemacht. Async und Await ist schon richtig. Hilft aber nicht alleine, wenn beim Button Click irgend ne CPU lastige Sache ausgeführt wird. Es kommt halt drauf an, was gemacht wird. IO Sachen, wo man vielleicht auf async await umstellen kann? Ansonsten muss natürlich die Logik im einen neuen Thread ausgeführt werden. Mach den Handler async Task und lass dort den Code in einem neuen Task Threadpool laufen: await Task.Run(() => ...)

Mr. Lolman
2018-12-06, 12:49:35
Dann hast du es vermutlich nicht richtig gemacht. Async und Await ist schon richtig. Hilft aber nicht alleine, wenn beim Button Click irgend ne CPU lastige Sache ausgeführt wird. Es kommt halt drauf an, was gemacht wird. IO Sachen, wo man vielleicht auf async await umstellen kann? Ansonsten muss natürlich die Logik im einen neuen Thread ausgeführt werden. Mach den Handler async Task und lass dort den Code in einem neuen Task Threadpool laufen: await Task.Run(() => ...)

Die externe Win32 Anwednung kann kein Async/Await. Die nimmt nur den Klick des eingebundenen WPF-Controls an und von da an läuft innerhalb der Anwendung die weitere singlethreaded-Verarbeitung.

xxxgamerxxx
2018-12-06, 16:45:55
Die externe Win32 Anwednung kann kein Async/Await. Die nimmt nur den Klick des eingebundenen WPF-Controls an und von da an läuft innerhalb der Anwendung die weitere singlethreaded-Verarbeitung.

Also liegt die Logik in der Win32 Anwendung? Dann sollte dort der neue Thread erzeugt werden. Storyboard.Begin blockt ja nicht.

Mr. Lolman
2018-12-06, 19:17:54
Also liegt die Logik in der Win32 Anwendung? Dann sollte dort der neue Thread erzeugt werden. Storyboard.Begin blockt ja nicht.

Ja, das hab ich mir auch schon überlegt. Tatsächlich würde das aber bedeuten, die komplette Anwendung umzubauen, nur damit das GUI nicht mehr blockiert wird. Da scheint mir die Lösung von Monger simpler zu sein.

xxxgamerxxx
2018-12-06, 21:39:35
Ja, das hab ich mir auch schon überlegt. Tatsächlich würde das aber bedeuten, die komplette Anwendung umzubauen, nur damit das GUI nicht mehr blockiert wird. Da scheint mir die Lösung von Monger simpler zu sein.

Und wie soll das funktionieren, wenn deine Logik in der Win32 Anwendung liegt?

Mr. Lolman
2018-12-07, 08:06:53
In dem die Anwendung die interne, singlethreaded Verarbeitung nicht direkt mit dem WPF-Button Klick Event startet, sondern mit einem neuem Customevent - was das WPF-Control nach Ablauf der Animation schickt.

xxxgamerxxx
2018-12-07, 09:16:34
In dem die Anwendung die interne, singlethreaded Verarbeitung nicht direkt mit dem WPF-Button Klick Event startet, sondern mit einem neuem Customevent - was das WPF-Control nach Ablauf der Animation schickt.

Das geht natürlich auch. Ich bin davon ausgegangen, dass die Animation "während" der Verarbeitung laufen sollte.

Dann wäre es Mit 2. Event lediglich eine sequentielle Abarbeitung im UI Thread. D.h. nachdem die Animation im Control abgespielt wurde, wird erst dann in der Win32 App weitergearbeitet und der UI Thread blockt dort mit Sanduhr.

Mr. Lolman
2018-12-12, 06:18:33
Genau so ist es. Da die Animation nur 0.5sec dauert, ist das aber verschmerzbar.