Animierte GIFs in PictureBox anhalten für Hover-Effekt

  • VB.NET

Es gibt 72 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Animierte GIFs in PictureBox anhalten für Hover-Effekt

    Guten Morgen,

    ich probiere gerade animierte GIFs in einer PictureBox "anzuhalten", und nur abzuspielen, wenn man mit der Maus drüberfährt. Wenn ich nun PictureBox1.Enabled = False mache, hält das Bild an, bei True spielt es ab. Einziges Problem: Wenn die PictureBox auf False ist, wird auch kein MouseHover-Ereignis erzeugt.

    Ist es irgendwie möglich, das ohne grösseren Aufwand zu umgehen?

    Hab zwar einen Post dazu gefunden, der ist aber von 2010...
    @kafffee Wer animiert denn die GIF?
    Das System oder Dein Programm?
    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!
    Ist es wichtig welcher Teil der angehaltenen Animation zu sehen ist?
    Wenn nicht dann könntest Du einfach die Bilder austauschen, also
    Animation gegen Standbild tauschen wenn der Mauszeiger drauf
    steht und zurück zur Animation wenn der Mauszeiger wieder
    woanders steht.
    Aktuelles Projekt: Z80 Disassembler für Schneider/Amstrad CPC :love:
    @RodFromGermany

    Ich hab das animierte GIF im Internet gefunden und dann einfach per .Image-Eigenschaft in die PictureBox geladen, ohne dass ich Code dafür geschrieben hab.

    @oobdoo

    Es soll das erste Frame als Stanbdbild gezeigt werden. Du meinst mit einem Bildbearbeitungsprogramm einfach das gewünschte Frame extrahieren und als Standbild speichern und dann mit der .Image-Eigenschaft bei MouseHover das Bild wechseln?

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

    @kafffee OK, dann probier mal so:
    Du kannst im IrfanView das Startbild der GIF extrahieren und als PNG der PictureBox als Image zuweisen.
    Im HOver weist Du der PictureBox die GIF als Image zu und merkst Dir das.
    Im Leave-Event AndAlso GIF zugewiesen weist Du der PictureBox wieder die PNG als Image zu.
    Übrigens:
    Nicht jede GIF wird vom Framework animiert.
    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
    @kafffee Probiere mal den ImageAnimator.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.ComponentModel
    4. Public Class Form1
    5. Private animGif As Bitmap
    6. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    7. animGif = New Bitmap("D:\Programming\VBC\GDI+\animake.gif")
    8. End Sub
    9. Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
    10. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    11. animGif.Dispose()
    12. End Sub
    13. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    14. ImageAnimator.UpdateFrames()
    15. Using bmpFrame As New Bitmap(animGif, animGif.Width, animGif.Height)
    16. e.Graphics.Clear(PictureBox1.BackColor)
    17. e.Graphics.DrawImage(bmpFrame, New Point(0, 0))
    18. End Using
    19. End Sub
    20. Private Sub PictureBox1_MouseEnter(sender As Object, e As EventArgs) Handles PictureBox1.MouseEnter
    21. ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))
    22. End Sub
    23. Private Sub PictureBox1_MouseLeave(sender As Object, e As EventArgs) Handles PictureBox1.MouseLeave
    24. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    25. End Sub
    26. Private Sub Animate(ByVal o As Object, ByVal e As EventArgs)
    27. PictureBox1.Invalidate()
    28. End Sub
    29. End Class


    Ansonsten kannst auch selbst alles per GDI+ zeichnen. GIF laden, Anzahl der Frames auslesen, Pausenzeiten zwischen den Frames auslesen usw usw und jedes Frame (Einzelbild) auf die PB zeichnen.
    Mfg -Franky-
    Vielleicht ein bisken Pfeil-Rücken-Brust-Auge, aber es klappt durch Eigenanimation
    Dateien
    • WinFormsVB.zip

      (21,3 kB, 70 mal heruntergeladen, zuletzt: )
    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.
    @RodFromGermany

    So cool die Idee auch ist und funktioniert, es gibt aber Probleme mit den transparenten "Farben". Diese werden nicht korrekt extrahiert. Wahrscheinlich mach ich was bei den Einstellungen falsch. Kannst du mit den Screenshots im Anhang was anfangen?

    @-Franky-

    Der Code sieht ja ganz gut aus. Aber wenn ich das in Bitmap konvertier wird wahrscheinlich auch nix mit Transparenz zu machen sein oder? Das mit GDI+ ist für mich komplettes Neuland. Gibts da ein Tutorial oder sowas?

    @VaporiZed

    Ich schau mir das mal an.
    EDIT: Leider kann ich das Projekt nicht starten. Ich musste es in ein niedrigeres Framework umwandeln und jetzt kommt der Fehler "Das Manifestsignaturzertifikat wurde nicht im Zertifikatspeicher gefunden."

    EDIT2: OK das Folgende hier genügt im Zusammenspiel mit PictureBox.Enabled völlig (aber da muss ich dann wahrscheinlich für jede PictureBox (welche sozusagen als "Buttons" verwendet werden) eine eigene MouseIsOverPicBox schreiben müssen oder?:

    VB.NET-Quellcode

    1. Private Function MouseIsOverPicBox(e As MouseEventArgs) As Boolean
    2. Return e.X >= PictureBox1.Left AndAlso e.X <= PictureBox1.Left + PictureBox1.Width AndAlso e.Y >= PictureBox1.Top AndAlso e.Y <= PictureBox1.Top + PictureBox1.Height
    3. End Function

    Bilder
    • gif einstellungen.jpg

      88,31 kB, 578×536, 54 mal angesehen
    • png einstellungen.jpg

      55,78 kB, 460×362, 59 mal angesehen

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „kafffee“ ()

    Ist hier jetzt mit FW 4.0
    Besser?

    Naja, oder der RohCode eben:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class FrmMain
    2. Private FrameCount As Integer = 0
    3. Private CurrentFrameIndex As Integer = 0
    4. Private Dimension As Drawing.Imaging.FrameDimension = Nothing
    5. Private Sub FrmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    6. Dimension = New Drawing.Imaging.FrameDimension(PicAnimatedGif.Image.FrameDimensionsList(0))
    7. FrameCount = PicAnimatedGif.Image.GetFrameCount(Dimension)
    8. End Sub
    9. Private Sub TmrAnimation_Tick(sender As Object, e As EventArgs) Handles TmrAnimation.Tick
    10. CurrentFrameIndex = (CurrentFrameIndex + 1) Mod FrameCount
    11. PicAnimatedGif.Image.SelectActiveFrame(Dimension, CurrentFrameIndex)
    12. PicAnimatedGif.Refresh()
    13. End Sub
    14. Private Sub FrmMain_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
    15. If MouseIsOverPicBox(e) Then
    16. ResumeAnimation()
    17. Else
    18. StopAnimation()
    19. End If
    20. End Sub
    21. Private Function MouseIsOverPicBox(e As MouseEventArgs) As Boolean
    22. Return e.X >= PicAnimatedGif.Left AndAlso e.X <= PicAnimatedGif.Left + PicAnimatedGif.Width AndAlso e.Y >= PicAnimatedGif.Top AndAlso e.Y <= PicAnimatedGif.Top + PicAnimatedGif.Height
    23. End Function
    24. Private Sub ResumeAnimation()
    25. If TmrAnimation.Enabled Then Return
    26. TmrAnimation.Start()
    27. End Sub
    28. Private Sub StopAnimation()
    29. TmrAnimation.Stop()
    30. End Sub
    31. End Class



    Brauchst eben einen Timer (TmrAnimation) und Deine Picturebox (PicAnimatedGif)

    ##########
    @kafffee:
    Nö, Du kannst ja auch der Function eine PicBox mitgeben:

    VB.NET-Quellcode

    1. Private Function MouseIsOverPicBox(PicBox As PictureBox, e As MouseEventArgs) As Boolean
    2. Return e.X >= PicBox.Left AndAlso e.X <= PicBox.Left + PicBox.Width AndAlso e.Y >= PicBox.Top AndAlso e.Y <= PicBox.Top + PicBox.Height
    3. End Function
    Dateien
    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“ ()

    @kafffee

    Aber wenn ich das in Bitmap konvertier wird wahrscheinlich auch nix mit Transparenz zu machen sein oder?

    Was genau meinst Du mit "Transparenz"? Bei einer "animierten" GIF (8bppIndexed) ist die transparente Farbe entweder global (für alle Frames) oder pro Frame in der ColorTable (256 Farben im ARGB-Format) festgelegt. Bedeutet das jedes Frame auch mit seinem transparenten Bereich auf die PB gezeichnet wird (verhält sich daher so wie bei einem 32bppARGB Bitmap). Du kannst aber nachträglich noch eine bestimmte Farbe transparent machen.

    VB.NET-Quellcode

    1. ​bmpFrame.MakeTransparent(Color.Red)


    Das mit GDI+ ist für mich komplettes Neuland. Gibts da ein Tutorial oder sowas?

    Im Prinzip ist der Code von @VaporiZed genau das was ich beschrieben habe. Alles was in .NET Standardmäßig mit Bitmap, Image, Graphics usw. zu tun hat, ist GDI+.
    Mfg -Franky-

    kafffee schrieb:

    Screenshots im Anhang
    Steht doch da:

    Wähle die Farbe des Haupthensters oder setze sie selbst.
    Musst Du halt ausprobieren.
    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!
    @VaporiZed
    Leider gibt es nun doch ein kleines Problem damit. Da die PictureBox auf einem TableLayoutPanel liegt (genau Rand auf Rand) funktioniert das mit dem MouseMove net so ganz. Die Animation geht weiter, auch wenn ich mit der Maus die PictureBox verlasse..

    @-Franky-
    Habs jetz mal mit dem ImageAnimator versucht. Das klappt ganz gut, nur scheint das Programm den .SizeMode = Zoom zu ignorieren. Gibt s da vielleicht ein zusätzliches Argument oder so? Und kann man vielleicht irgendwie festlegen, dass das GIF immer bis "zum Ende" abgespielt wird, damit also dann wenn es stoppt immer das Startframe angezeigt wird?

    @RodFromGermany
    Mein Fehler, ich hab beim Festlegen der transparenten Farbe (was ich mit einem Online-Programm gemacht habe) aus Versehen FloodFill statt ReplaceColor angeklickt. Ergebnis kannst du dir vorstellen.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „kafffee“ ()

    Ja gut, dann musst Du den Code eben so anpassen, dass eben nicht das Form als Referenz herhält, sondern das TableLayoutPanel.
    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.
    @VaporiZed
    Ah ja klar natürlich. Also ein Form1_Move und gleichzeitig ein TabelLayoutPanel1_Move verwenden... Leider hab ich deinen Spoiler auch mit dem Rohcode noch nicht zum Laufen gebracht. Bevor ich an der Stelle mal weitermache: Spielt der denn das GIF dann immer bis zum Ende ab oder, wenn man mit der Maus die PictureBox verlässt, verharrt es dann auf dem gerade angezeigten Frame (wie es ist wenn ich mit PictureBox.Enabled arbeite...) Das ist bei manchen GIFS irreführend, weil man dann die Funktion des "Buttons" nicht mehr versteht...

    kafffee schrieb:

    Also ein Form1_Move und gleichzeitig ein TabelLayoutPanel1_Move verwenden
    ist Tinnef. Du musst nur das äußere Control abfangen, das innere bewegt sich zum äußeren nicht.
    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!
    @kafffee
    ​ nur scheint das Programm den .SizeMode = Zoom zu ignorieren

    Der SizeMode funktioniert nur, wenn man der PB direkt ein Bitmap/Image zuweist und nicht für das zeichnen eines Bitmaps/Image in das Graphics-Objekt der PB. Wenn dann müsstest eine Skalierung berechnen der das Einzelframe entsprechend proportional sklaliert zeichnen.
    ​ kann man vielleicht irgendwie festlegen, dass das GIF immer bis "zum Ende" abgespielt wird

    Dachte die GIF soll stoppen wenn die Maus die PB verlässt. Wenn die GIF bis zum Ende abgespielt werden soll, auch wenn die Maus die PB bereits verlassen hat, dann musst das ganze dann doch so programmieren wie es @VaporiZed gezeigt hat. GIF laden, Anzahl der Frames ermitteln, Pausenzeiten zwischen den Frames ermitteln (Sonst spielt die GIF unter Umständen zu schnell oder zu langsam ab wenn man dem Timer eine feste Zeit vorgibt bzw anders ausgedrückt, ein Frame hat evtl eine Pausenzeit von 10ms und ein anderes 50ms das nachste dann wieder 10ms usw usw, wenn das immer mit der gleichen Zeit (feste zeit für den Timer) abgespielt wird, ist es zwar flüssig abgespielt, aber eben nicht korrekt von der Geschwindigkeit her.) Frame anhand des Index selektieren, auf die PB zeichnen oder auch direkt als Bitmap zuweisen (ZoomMode), ausgegebene Frames zählen bis die Anzahl der ermittelten Frames erreicht ist und zu guter letzt das erste Frame wieder anzeigen.

    Oder wenn Du beim ImageAnimator bleibst (der macht das mit der Pausenzeit zwischen den Frames automatisch), auch erst die Anzahl der Frames im GIF ermitteln und die ausgegebenen Frames zählen. Sind alle Frames ausgegeben, dann erst die Wiedergabe stoppen (StopAnimate) bzw dann noch ein UpdateFrames damit wieder das erste Frame zu sehen ist.
    Mfg -Franky-
    @kafffee

    Hier mal so meine Idee umgesetzt mit dem ImageAnimator (Stop nach dem kompletten Abspielen des GIF und Zoom)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.ComponentModel
    4. Imports System.Drawing.Imaging
    5. Public Class Form1
    6. Private animGif As Bitmap
    7. Private animFrameCount As Integer
    8. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    9. animGif = New Bitmap("D:\Programming\VBC\GDI+\animake.gif")
    10. animFrameCount = animGif.GetFrameCount(New FrameDimension(animGif.FrameDimensionsList(0)))
    11. End Sub
    12. Private Sub Form1_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
    13. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    14. animGif.Dispose()
    15. End Sub
    16. Private Sub PictureBox1_MouseEnter(sender As Object, e As EventArgs) Handles PictureBox1.MouseEnter
    17. ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))
    18. End Sub
    19. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    20. Static animCurrentFrame As Integer
    21. ImageAnimator.UpdateFrames()
    22. animCurrentFrame += 1
    23. If animCurrentFrame > animFrameCount Then
    24. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    25. animCurrentFrame = 1
    26. End If
    27. Using bmpFrame As New Bitmap(animGif, animGif.Width, animGif.Height)
    28. e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    29. e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
    30. e.Graphics.Clear(PictureBox1.BackColor)
    31. Dim dblRatio As Double = Math.Min(Convert.ToDouble(PictureBox1.Width) / animGif.Width,
    32. Convert.ToDouble(PictureBox1.Height) / animGif.Height)
    33. e.Graphics.DrawImage(bmpFrame, 0, 0, Convert.ToSingle(animGif.Width * dblRatio),
    34. Convert.ToSingle(animGif.Height * dblRatio))
    35. End Using
    36. End Sub
    37. Private Sub Animate(ByVal o As Object, ByVal e As EventArgs)
    38. PictureBox1.Invalidate()
    39. End Sub
    40. End Class

    Mfg -Franky-

    kafffee schrieb:

    Spielt der denn das GIF dann immer bis zum Ende ab oder, wenn man mit der Maus die PictureBox verlässt, verharrt es dann auf dem gerade angezeigten Frame
    Ich hatte das so eingebaut:
    • GIF steht zu Beginn still
    • Maus über die PicBox -> GIF wird animiert
    • Maus verlässt PicBox -> GIF stoppt bei aktuellem Bild X
    • Maus über die PicBox -> Animation wird fortgesetzt ab Bild X
    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.

    RodFromGermany schrieb:

    ist Tinnef


    Was meinst du mit Tinnef?

    @-Franky-

    Super genauso hab ich mir das vorgestellt. Sehr cool auch die Möglichkeit in Zeile 47 sozusagen das Bild, zu dem "zurückgekehrt" wird, festzulegen... Das Ganze funktioniert aber nur wenn man das GIF direkt über einen Pfad lädt. Wenn man es aus den Ressourcen verwendet wird da nichts animiert. Gibt es eine Möglichkeit die Dateien ins Programmverzeichnis ausserhalb der Ressourcen mitzugeben und dann den Anwendungspfad mittels Code ermitteln und daraus zu laden? Ich mein für mich wär das kein Problem, aber wenn ich das Programm weitergeben möchte, kann ich nicht von einem Laien erwarten, dass er die GIFS ins Programmverzeichnis kopiert, zumal man bei meinem Setup-Programm nicht mal das Installationsverzeichnis auswählen kann..

    Edit: Ich hab das jetzt so gelöst:

    VB.NET-Quellcode

    1. Dim img As Image = My.Resources.Add
    2. img.Save("C:\Add.gif", Imaging.ImageFormat.Gif)
    3. animGif = New Bitmap("C:\Add.gif")


    Aber vielleicht funktioniert das ja, wenn ich die Resource als Verknüpfung speichere und nicht mit einkompiliere. Hab aber leider keine entsprechende Option gefunden...

    Edit:
    Und noch was: Das Ereignis PictureBox1_Paint wird nicht beim Programmstart ausgelöst oder? Ich versuche gerade sozusagen ein "Startframe" festzulegen, das beim Laden der Form angezeigt wird. Nun habe ich Static animCurrentFrame umgeändert in Public CurrentFrame und in Public Class Form1 gepackt und den Wert in Form_Load auf 10 für das zehnte Frame gesetzt. Es wird wie gewünscht animiert, nur wird halt beim Start des Programms das "falsche" Frame angezeigt. Sollte ich da einfach in Form_Load das Ereignis PictureBox1_Paint auslösen oder/und wie kann ich das bewerkstelligen?

    Dieser Beitrag wurde bereits 15 mal editiert, zuletzt von „kafffee“ ()