Animierte GIFs in PictureBox anhalten für Hover-Effekt

  • VB.NET

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

    Tinnef entspricht ungefähr "Blödsinn" - einfache Suche bei einer Suchmaschine Deiner Wahl.
    In Post#9 habe ich den Spaß nochmal mit Framework 4.0 hochgeladen. Da noch niemand es runtergeladen hat, hast Du die Variante also noch nicht geprüft. Schau ggf. mal rein. Oder reicht 4.0 nicht?
    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.
    @kafffee Vergebliche Lebensmüh.
    Das innere Control hat bei Bewegung seines Parents stets fixe Koordinaten, da kommt kein .Move-Event. Hier nur das der Form.
    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
    ​Sehr cool auch die Möglichkeit in Zeile 47 sozusagen das Bild, zu dem "zurückgekehrt" wird, festzulegen...

    Ist eigentlich kein Festlegen des Startbildes. Ist eher sowas wie wenn animCurrentFrame > animFrameCount ist, ist ja schon wieder das erste Frame gezeichnet, die Animation wird gestoppt und animCurrentFrame = 1 (erstes Frame) gesetzt.
    ​Das Ganze funktioniert aber nur wenn man das GIF direkt über einen Pfad lädt

    Nö, hab mal meine GIF als Ressource eingebunden und dann mit

    VB.NET-Quellcode

    1. ​animGif = My.Resources.animake

    geladen.
    ​Und noch was: Das EreignisPictureBox1_Paint wird nicht beim Programmstart ausgelöst oder?

    Japp wird er. Sonst würde ja das erste Frame aus der GIF nicht zu sehen sein.
    für das zehnte Frame gesetzt... ​das "falsche" Frame angezeigt.

    Hmm, ein StartFrame festlegen geht mit dem ImageAnimator nicht. Sobald Du StopAnimate und dann wieder Animate aufrufst, wird automatisch beim ersten Frame angefangen. In dem Fall müsstest dann doch auf den Code von VaporiZed ausweichen. Hier kannst ja über PicAnimatedGif.Image.SelectActiveFrame(Dimension, CurrentFrameIndex) einen Index mit übergeben der als StartFrame angezeigt werden soll.
    Mfg -Franky-

    -Franky- schrieb:

    Nö, hab mal meine GIF als Ressource eingebunden und dann mit
    VB.NET-Quellcode
    animGif = My.Resources.animake

    geladen.


    Hm, vielleicht liegts am GIF selbst. Der @RodFromGermany hat ja glaub ich geschrieben, dass nicht alle Animated GIFS angezeigt werden...

    -Franky- schrieb:

    In dem Fall müsstest dann doch auf den Code von VaporiZed ausweichen


    Und was ist wenn ich einfach den Code von PictureBox1_Paint einfach ins Form1_Load packe? Natürlich modifiziert. Dann hab ich halt beim Programmstart eine ganz kurze Animation (bis ich beim gewünschten Frame bin). Das wäre denke ich OK für mich, könnte das Ganze vielleicht auch sogar im besten Fall etwas lebendiger aussehen lassen...

    Ich hab das so gemacht, aber da ist noch ein (Denk-)Fehler drin.

    VB.NET-Quellcode

    1. Private Sub Hauptseite_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. Dim img As Image = My.Resources.Add
    3. img.Save("C:\Add.gif", Imaging.ImageFormat.Gif)
    4. animGif = New Bitmap("C:\Add.gif")
    5. animFrameCount = animGif.GetFrameCount(New FrameDimension(animGif.FrameDimensionsList(0)))
    6. ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))
    7. ImageAnimator.UpdateFrames()
    8. animCurrentFrame += 1
    9. If animCurrentFrame = 10 Then
    10. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    11. End If
    12. End Sub


    EDIT: Habs mal wieder durch raten rausgefunden. Einfach ab Zeile 6:

    ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))

    und Startframe schon bei der Deklaration festlegen:

    Public animCurrentFrame As Integer = 10

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

    kafffee schrieb:

    hat ja glaub ich geschrieben, dass nicht alle Animated GIFS angezeigt werden...
    Nicht "nicht angezeigt" sondern "nicht animiert".
    Ich hab mal ein Tut verzapft zur Komposition von GIFs, die werden nicht animiert.
    Tutorial: Animated Gif erstellen
    Es gibt andere GIF-Kompositoren, da kann man auch den individuellen Image-Takt vorgeben.
    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 Wenn Du ImageAnimator.StopAnimate und danach irgandwann wieder ImageAnimator.Animate, fängt die GIF wieder mit dem ersten Frame an. Da kannste machen nix. ;) Nimm dem Code von VaporiZed. Der ist fexibler als mein Code.

    @RodFromGermany
    Nicht "nicht angezeigt" sondern "nicht animiert".Ich hab mal ein Tut verzapft zur Komposition von GIFs, die werden nicht animiert.​

    Wenn Du die Windows Imaging Componet (WIC) verwendest, kannst Du animierte GIFs erstellen und über ein Interface IMetadataQueryWriter diverse Eigenschaften für eine animierte GIF festlegen. Unter anderem per Metatag "/grctlext/Delay" zb die Pausenzeit zwischen den Frames oder per Metatag "/appext/Data" legt man fest ob die GIF eine animierte GIF ist und den LoopCount oder per Metatag "/commentext/TextEntry" könnte man auch noch zu jedem Frame einen Text hinterlegen. Gibt noch eine Reihe anderer Metatags die man für eine animierte GIF schreiben könnte.
    Mfg -Franky-
    Problem (fast) gelöst...

    Zur nächsten Frage:

    Ich würde gerne, wie gelernt, der folgenden Prozedur die jeweilige PictureBox übergeben. In etwa so:

    VB.NET-Quellcode

    1. Private Sub Animate(PicBox As PictureBox, ByVal o As Object, ByVal e As EventArgs)
    2. PicBox.Invalidate()
    3. End Sub


    Leider wird beim Aufrufen:

    ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate(PictureBox1)))

    Das Me.Animate (also eigentlich nur das Animate) rot unterstrichen. Der Editor schlägt mir nur vor, dass ich eine Methode oder Eigenschaft generiere...
    @RodFromGermany falls Du Dich dafür interessierst, hier gibt es einen Link zu den möglichen Metatags die man für eine "animierte" GIF festlegen oder auch auslesen kann. docs.microsoft.com/en-us/windo…data-queries#gif-metadata -> GIF Metadata -> Global Metadata und Frame Metadata
    Mfg -Franky-

    kafffee schrieb:

    Me.Animate
    Da musst Du genau die Parameter hinten dran hängen, die erwartet werden.
    @-Franky- Danke für die Info, das damals war bloß eine Fingerübung.
    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!

    RodFromGermany schrieb:

    Da musst Du genau die Parameter hinten dran hängen, die erwartet werden


    Also o und e noch hinter PictureBox1. Aber was für einen Wert geb ich dann denen beiden gibts da kein Default oder sowas?

    @-Franky-

    Wenn ich nun mehrere PictureBoxes habe, die alle animiert werden sollen, dann muss ich zusätzliche Instanzen des ImageAnimators anlegen stimmt das? Weil ich hab das Ganze jetzt mal mit einer weiteren PictureBox auf der Form probiert, die scheinen dann immer an einem zufälligen Punkt stehen zu bleiben. Ich vermute das passiert wenn der ImageAnimator sich selbst in die Quere kommt...

    Das Ganze ist jetzt schon so viel Code wenn ich das für jede PictureBox in mein Projekt integriere dann blick ich gar nicht mehr durch. Ich denke drüber nach einfach ein externes Steuerelement daraus zu machen, was haltet ihr davon?
    @kafffee

    Wenn Du das für mehrere PictureBoxen einsetzen möchtest, ist das ganze doch prädestiniert für eine Klasse die den ganzen Code enthält. Entsprechend wird dann für jede PB eine Instanz dieser Klasse erstellt. Entweder Du übergibst entsprechende Parameter (GIF, StartFrame, PictureBox usw) bereits im Constructor der Klasse oder später per Properties. Eine Sub/Funktion zum starten oder stoppen der Animation und was man noch so braucht (Events usw).
    Mfg -Franky-
    @-Franky-

    Ich hatte jetzt eher an ein Benutzersteuerelement gedacht, das ich dann einfach per DLL einbinde und durch das ich dann einfach meine PictureBoxen ersetze. Ist noch einfacher und besser portabel als eine "normale Klasse" denke ich...

    Edit: Wo pack ich denn dann den Code aus Form1_Closing rein? Ich hab das bei AnimGifButton_Dispose (so hab ich das Control genannt) reingetan und bekomm ungefähr tausend Fehlermeldungen wenn ich das Control auf die Form ziehen will. Ich glaube die ham alle was damit zu tun...

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

    @kafffee
    Das überlasse ich Dir ob Du ein Control (DLL) draus machst. Ich wäre für eine ganz normale Klasse. Vllt eine Klasse die von PictureBox erbt (auch ein Control ohne DLL) mit erweiterten/zusätzlichen Properties. Warum? Einfacheres Debugging, Haltepunkte setzen, Code im Einzelschritt durchsteppen usw usw. What ever. Tja Fehlermeldungen, und welche? Code von Deinem Control?
    Mfg -Franky-

    kafffee schrieb:

    Also o und e noch hinter PictureBox1
    Wo kommt denn der Aufruf her?
    Ich denke mal, Du kannst die Parameter weglassen, lösche sie in der Prozedur Animate() und dann auch bei allen Aufrufern.
    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!
    @-Franky-

    Fehlermeldungen findest du im Anhang. Code sieht so aus:

    VB.NET-Quellcode

    1. Option Strict On
    2. Imports System.IO
    3. Imports System.ComponentModel
    4. Imports System.Drawing.Imaging
    5. Public Class AnimGifButton
    6. Private animGif As Bitmap
    7. Private animFrameCount As Integer
    8. Private animCurrentFrame As Integer
    9. Private Image As String = ""
    10. Private Start As Integer
    11. Private Stopp As Integer
    12. Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click
    13. End Sub
    14. Public Property GIFPath As String
    15. Get
    16. Return Image
    17. End Get
    18. Set(value As String)
    19. Image = value
    20. End Set
    21. End Property
    22. Public Property StartFrame As Integer
    23. Get
    24. Return Start
    25. End Get
    26. Set(value As Integer)
    27. Start = value
    28. End Set
    29. End Property
    30. Public Property StopFrame As Integer
    31. Get
    32. Return Stopp
    33. End Get
    34. Set(value As Integer)
    35. Stopp = value
    36. End Set
    37. End Property
    38. Private Sub AnimGifButton_Load(sender As Object, e As EventArgs) Handles Me.Load
    39. animCurrentFrame = Start 'Initialisierung
    40. 'Dim imgAdd As Image = My.Resources.Add
    41. 'imgAdd.Save("C:\Users\Alpha\Desktop\20210307-1\EQTest\EQTest\bin\Debug\Add.gif", Imaging.ImageFormat.Gif)
    42. animGif = New Bitmap(Image)
    43. animFrameCount = animGif.GetFrameCount(New FrameDimension(animGif.FrameDimensionsList(0)))
    44. ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))
    45. End Sub
    46. Private Sub AnimGifButton_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
    47. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    48. animGif.Dispose()
    49. End Sub
    50. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    51. ImageAnimator.UpdateFrames()
    52. animCurrentFrame += 1
    53. If animCurrentFrame > animFrameCount Then
    54. ImageAnimator.StopAnimate(animGif, New EventHandler(AddressOf Me.Animate))
    55. animCurrentFrame = Stopp
    56. End If
    57. Using bmpFrame As New Bitmap(animGif, animGif.Width, animGif.Height)
    58. e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
    59. e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
    60. e.Graphics.Clear(PictureBox1.BackColor)
    61. Dim dblRatio As Double = Math.Min(Convert.ToDouble(PictureBox1.Width) / animGif.Width,
    62. Convert.ToDouble(PictureBox1.Height) / animGif.Height)
    63. e.Graphics.DrawImage(bmpFrame, 0, 0, Convert.ToSingle(animGif.Width * dblRatio),
    64. Convert.ToSingle(animGif.Height * dblRatio))
    65. End Using
    66. End Sub
    67. Private Sub PictureBox1_MouseEnter(sender As Object, e As EventArgs) Handles PictureBox1.MouseEnter
    68. ImageAnimator.Animate(animGif, New EventHandler(AddressOf Me.Animate))
    69. End Sub
    70. Private Sub Animate(ByVal o As Object, ByVal e As EventArgs)
    71. PictureBox1.Invalidate()
    72. End Sub
    73. End Class


    EDIT: Habs hinbekommen, indem ich den Varaiablen in Zeile 11 - 13 gleich bei der Deklaration gültige Werte gegeben hab.
    Bilder
    • Fehlermeldungen.jpg

      116,2 kB, 420×520, 26 mal angesehen

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

    @-Franky-

    Also so richtig klappen will das nicht. Mein Visual Studio macht mir Probleme. Siehe Screenshot. Das ist mir alles zu "unsicher"...

    Wie funktioniert denn das mit den vererbten Klassen? ich glaube ich versuch das dann doch mal damit. Wo fang ich da an und wo hör ich auf?
    Für jede PicBox eine Instanz erstellen>klar
    Im Konstruktor die Parameter übergeben >klar

    Wo es dann bei mir z. B. aufhört ist:

    Kann ich auch Events vererben? Also dass ich nicht im PictureBox1_MouseEnter-Event dann eine Methode Meine Klasse.Animiere() aufrufen muss sondern das automatisch innerhalb der Klasse passiert?
    Bilder
    • Fehlermeldung Control.jpg

      394,04 kB, 1.600×900, 26 mal angesehen

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

    @kafffee In AnimGifButton_Load greifst Du auf einen leeren Pfad zu.
    Teste das und erstelle das Bild später in der Property.
    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 schrieb:

    Screenshot von meinem letzten Post.
    Was ist zu tun, um das zu reproduzieren?
    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 schrieb:

    Wie funktioniert denn das mit den vererbten Klassen? ich glaube ich versuch das dann doch mal damit. Wo fang ich da an und wo hör ich auf?

    Wenn Du das nur mal auf die schnelle testen möchtest, kommt die Klasse AnimGifButton direkt hinter "End Class" der MainForm. Also keine extra Klasse.

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Public Class MainForm
    4. End Class
    5. Public Class AnimGifButton
    6. Inherits PictureBox
    7. End Class

    nach dem Einfügen des Klasse AnimGifButton direkt hinter "End Class" der Form, das Programm einmal starten! Dann findest Du ganz oben in der Toolbox ein Control AnimGifButton das Du wie gewohnt auf der Form platzieren kannst. Dieses Control erbt von der PicturBox und besitzt somit auch alle Eigenschaften und Events der Picturebox. Innerhalb der Klasse AnimGifButton kannst Du nun weitere Properties/Events anlegen, auf die Events der Picturebox zugreifen, vorhandene Properties der PictureBox erweitern usw usw.
    Mfg -Franky-