Event durch sich selbst limitieren

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

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    Event durch sich selbst limitieren

    Hallo,

    ich habe ein Event, das unter Umständen recht oft auslöst, was aber recht überflüssig ist und dennoch Einiges an Arbeit in Gang setzt.
    Deswegen hab ich ganz simpel erstmal geschaut, ob das letzte Event denn lang genug her ist.

    VB.NET-Quellcode

    1. Dim EventTS = Date.Now
    2. If EventTS - LastEventTS < TimeSpan.FromMilliseconds(500) Then Exit Sub
    3. LastEventTS = Date.Now
    Da habe ich aber nun das Problem, dass ich nicht das letzte Event erfassen kann, wenn es zu dicht an den anderen liegt.

    Als Beispiel die Events kommen
    1. 00:00:00.010
    2. 00:00:00.215
    3. 00:00:00.402

    In diesem Fall erfasse ich nur das erste Event. Stattdessen möchte nur das letzte Event erfassen.

    Das heißt ich brauche eigentlich im Event einen Timer der bis 500ms wartet und dann erst den Rest erlaubt, aber aus einem neueren Event heraus abgebrochen werden kann und somit das alte Event beendet.
    Das denke ich ist allerdings nicht möglich, da die Events ja der Reihe nach abgearbeitet werden.

    Was denkt ihr?

    Viele Grüße
    Ich hoffe, dass Du die Begrifflichkeiten durcheinanderwirbelst.
    Das Event ist der Hinweis, dass was passiert ist. Ich vermute mal, dass der EventHandler da sehr viel macht und nicht zu oft aufgerufen werden soll.
    Aber vom Prinzip her klappt die Wunschbeschreibung m.E. mit Deinem Code. Als Beispiel: Form_MouseMove plus ListBox:

    VB.NET-Quellcode

    1. Private LastRegisteredTimeStamp As Date
    2. Private Sub FrmMain_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
    3. If (Date.Now - LastRegisteredTimeStamp).TotalSeconds < 0.5 Then Return
    4. ListBox1.Items.Add(Date.Now.ToString)
    5. LastRegisteredTimeStamp = Date.Now
    6. End Sub

    Das MouseMove-Event wird zigmal gefeuert, aber nur, wenn seit dem letzten akzeptierten Event 0.5 Sekunden vergangen sind, wird ein neuer Eintrag in die LB geschrieben.

    Ach Moment. Du willst, dass seit dem letzten Eventauftreten mindestens 0,5 Sekunden vergangen sind. Und wenn doch noch ein Eventabfeuern dazukommt, aber die 0,5 Sekunden Pause noch nicht rum sind, dann soll nix passieren, sondern der DelayTimer wieder auf Null gesetzt werden. Mal überlegen, welchen praktischen Unterschied das zum bisherigen Code macht …
    Also soll, wenn man die Maus bewegt, solange kein EventHandler aufgerufen werden, solange die Maus oft genug bewegt wird. Sondern nur dann, wenn seit der letzten Mausbewegung mindestens 0,5 Sekunden vergangen sind. Hm.

    Ja dann:

    VB.NET-Quellcode

    1. If (Date.Now - LastRegisteredTimeStamp).TotalSeconds < 0.5 Then LastRegisteredTimeStamp = Date.Now : Return

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „VaporiZed“ ()

    Ja nun stell dir vor du wolltest die Mausposition in die Listbox schreiben.
    Du willst die Mauspositionen haben, die sich mindestens 500ms gehalten haben.

    Durch die Überprüfung (Date.Now - LastRegisteredTimeStamp).TotalSeconds < 0.5 wird kategorisch ein Handling-Prozess quittiert, wenn er sich ein paar Millisekunden hinter einem nicht quittierten befindet.
    Stattdessen muss er quittiert werden, wenn er sich zu kurz vor einem Neuen befindet. Ich weiß das klingt wie Hellseherei.

    1. 00:00:00.010, Position: x = 0
    2. 00:00:00.215, Position: x = 1
    3. 00:00:00.402, Position: x = 2
    4. 00:00:01.322, Position: x = 3
    5. 00:00:02.155, Position: x = 4
    6. 00:00:02.455, Position: x = 5

    Hier speicherst du, denke ich: x = 0,3,4
    Und ich wollte x = 2,3,(eventuell 5)

    (Sidenote: Ich weiß nicht, ob sich die MouseEventArgs so verhalten. Bei mir ist der Zustand relevant der bei Auslösen des Events vorliegt. Dieser Zustand ist also bei jedem Auslösen anders. Der Handler soll nur nicht auf jeden eingehen, wenn dann aber auf den Aktuellen)

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Meinst du vielleicht ein Verhalten wie der debounceTime-Operator im Umfeld reaktiver Programmierung? „Emits a notification from the source Observable only after a particular time span has passed without another source emission.“

    Also „das letzte Event, wenn seit $Zeit kein weiteres Event gefeuert worden ist“?
    Bilder
    • debounceTime.png

      49,2 kB, 1.280×540, 65 mal angesehen
    Mit freundlichen Grüßen,
    Thunderbolt
    Das, was ich spezifisch verlinkt habe ist JavaScript, also für dich im .NET-Umfeld so nicht zu gebrauchen. Aber es diente uns als Kommunikationsgrundlage, sodass ich jetzt verstanden habe, was überhaupt dein Ziel ist.

    Es gibt ähnliche Libraries natürlich auch für .NET; allerdings bin ich der Meinung, dass man nicht rechtfertigen kann, für diesen einen Use-Case eine doch recht umfangreiche Dependency ins Projekt zu holen. Also selber bauen.

    Ein paar Ideen dazu:
    Es bietet sich an, die Funktionalität in einer eigenen Klasse zu implementieren (Wiederverwendbarkeit). Dort kann über Methodenaufruf die Information zu einem neuen Event geteilt werden. Die Methode setzt dann einen Timer zurück. Effekt: Wenn der Timer lange genug nicht zurückgesetzt wird (= lange genug kein erneutes Event auftrat), löst er aus. Der Callback kann dann wiederum ein „entprelltes“ Event deiner Klasse auslösen.
    Mit freundlichen Grüßen,
    Thunderbolt
    Ich habe noch einen kleinen Trick gefunden. Um die aktuellen Daten zu verarbeiten brauche ich nicht das aktuelle Event. Das Event was bei mir durchläuft, verarbeitet einfach die Daten eines Aktuelleren.

    VB.NET-Quellcode

    1. Private currentEArgs as EventArgs
    2. Private Async Sub EventHandler(sender As Object, e As EventArgs) Handles Event
    3. currentEArgs = e
    4. Dim EventTS = Date.Now
    5. If (EventTS - LastEventTS).TotalMilliseconds < DelayCheckRoutine Then Exit Sub
    6. LastEventTS = Date.Now
    7. Await Task.Delay(DelayCheckRoutine)
    8. '...mit currentEArgs arbeiten
    9. End Sub