Sizechange einer Form (style=none) ohne neu zeichnen der gezeichneten Elementen (wegen flackern)

  • VB.NET
  • .NET (FX) 4.0

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    Sizechange einer Form (style=none) ohne neu zeichnen der gezeichneten Elementen (wegen flackern)

    Hi Community :)

    Da ihr mir so gut in letzter Zeit helfen konnten, wollte ich einen Lösungsansatz für mein neuestes Dilemma.
    (seht auch Anhänge)

    Ausgangslage:
    1. Form mit Borderstyle none
    2. Panels am Rand, mit denen man die die Grösse der Form ändern kann
    3. gezeichnete Schatten im Form.Paint Event

    Problemstellung:
    Wenn ich die Formgrösse ändere (was tadellos so funktioniert mithilfe eines timers), verändern sich die initial gezeichneten Schatten im Hintergrund nicht.
    Darum habe ich im Form.sizechanged Event, Me.invalidate() eingefügt, um es jedesmal neu zu zeichnen.
    Wie sich einige von euch denken können, flackert das gewaltig. Obschon me.doublebuffered auf True ist!

    Die Fragen:
    A. Kennt jemand von euch eine Möglichkeit, die gezeichneten Schatten ohne invalidate der Grösse der momentanen Form anzupassen?
    B. Oder kennt jemand eine Möglichkeit, wie man das effizienter lösen kann?

    Vielen Dank schon im Voraus! :thumbup:

    Noch die Codes, fürs bessere Verständnis:

    Der Resize Code im Timer Tick Event:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'dim x2 as integer = 0
    2. 'dim y2 as integer = 0
    3. 'dim alignside as integer = 0
    4. 'bei den Panels gebe ich mousedown event einfach nur die alignside mit und timer start, und im mouseup event timer stop
    5. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    6. If alignside = 0 Then
    7. x2 = Me.Location.X - MousePosition.X
    8. y2 = MousePosition.Y - Me.Location.Y
    9. Me.Width = -x2
    10. Me.Height = y2
    11. ElseIf alignside = 1 Then
    12. x2 = Me.Location.X - MousePosition.X
    13. y2 = MousePosition.Y - Me.Location.Y
    14. Me.Width = -x2
    15. ElseIf alignside = 2 Then
    16. x2 = Me.Location.X - MousePosition.X
    17. y2 = MousePosition.Y - Me.Location.Y
    18. Me.Height = y2
    19. ElseIf alignside = 3 Then
    20. x2 = Me.Location.X - MousePosition.X
    21. y2 = MousePosition.Y - Me.Location.Y
    22. Me.Width = Me.Width - -x2
    23. Me.Location = New Point(Me.Location.X + -x2, Me.Location.Y)
    24. End If
    25. End Sub



    Der Form Paint Event:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. e.Graphics.DrawImage(My.Resources.shadow01, -250, 0, 500, Me.Height)
    2. e.Graphics.DrawImage(My.Resources.shadow02, Me.Width - 250, 0, 500, Me.Height)
    3. e.Graphics.DrawImage(My.Resources.shadow03, 0, -250, Me.Width, 500)
    4. e.Graphics.DrawImage(My.Resources.shadow04, 0, Me.Height - 250, Me.Width, 500)


    Der Form Load Event:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub main_window_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. Me.MaximumSize = New Size(My.Computer.Screen.WorkingArea.Width, My.Computer.Screen.WorkingArea.Height)
    3. Me.DoubleBuffered = True
    4. Call form_shadow.getClassLong(Me.Handle.ToInt32, form_shadow.GCL_STYLE, form_shadow.SetClassLong(Me.Handle.ToInt32, form_shadow.GCL_STYLE) Or form_shadow.CS_DROPSHADOW) 'formschatten
    5. End Sub


    Der Form SizeChanged Event:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub main_window_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
    2. Me.Invalidate()
    3. End Sub
    Bilder
    • Initialform.PNG

      144,57 kB, 1.209×721, 197 mal angesehen
    • Form nach verändern der Grösse.png

      101,62 kB, 592×660, 164 mal angesehen
    • form_ohne_schatten.PNG

      48,77 kB, 1.173×693, 192 mal angesehen
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „M-Stef“ ()

    M-Stef schrieb:

    Me.invalidate()
    hat Überladungen.
    Wenn Du weißt, wo der Schatten liegt und wo er danach liegen soll, genügt es. diese beiden Regionen zu invalidisieren, da bleibt der Rest der Form unberührt von.
    Musst Du natürlich für jeden Schatten einzeln machen.
    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!
    Ja das habe ich schon probiert. Leider ist das in diesem Fall nicht wirklich besser, denn die Form wird zeitgleich mit dem Intervall(10ms) des Timers für ungültig erklärt und neu gezeichnet. Und da es kein, "SizeChangedFinished" Event gibt, kann man das vielleicht gar nicht effizient so lösen.

    Ausser vielleicht im MouseUp Event der Resize Panels, aber das Problem hierbei ist, dass man halt solange man die Grösse ändert, in mitten der Form den Schatten sieht. Auch nicht sehr sauber.

    Region Code: (nur der linke Schatten)

    VB.NET-Quellcode

    1. Me.Invalidate(New Rectangle(New Point(0, 0), New Size(500, me.size.height)))
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „M-Stef“ ()

    M-Stef schrieb:

    nicht wirklich besser
    Klar, wenn Du da die gesamte Form invalidisierst.
    Du musst das Häppchenweise tun, und ersrtell Dir eine Region (das ist eine Klasse vom Framework), in der Du die einzelnen Rechtecke reinpackst.
    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:

    M-Stef schrieb:

    nicht wirklich besser
    Klar, wenn Du da die gesamte Form invalidisierst.
    Du musst das Häppchenweise tun, und ersrtell Dir eine Region (das ist eine Klasse vom Framework), in der Du die einzelnen Rechtecke reinpackst.


    Ist das nicht das Gleiche ->Me.Invalidate(New Rectangle(New Point(0, 0), New Size(500, me.size.height))) ?
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    M-Stef schrieb:

    Ist das nicht das Gleiche
    Ich meine so was:

    VB.NET-Quellcode

    1. Me.Invalidate(New Rectangle(New Point(0, 0), New Size(50, 50)))
    2. Me.Invalidate(New Rectangle(New Point(100, 100), New Size(50, 50)))
    3. Me.Invalidate(New Rectangle(New Point(200, 200), New Size(50, 50)))
    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!
    Ja, das habe ich ja geschrieben?

    VB.NET-Quellcode

    1. ​Me.Invalidate(New Rectangle(New Point(0, 0), New Size(250, me.size.height))) 'linker Schatten
    2. Me.Invalidate(New Rectangle(New Point(me.size.width - 250, 0), New Size(250, me.size.height))) ' rechter Schatten
    3. Me.Invalidate(New Rectangle(New Point(0, me.size.height - 250), New Size(me.size.width, 250)) ' unterer Schatten
    4. Me.Invalidate(New Rectangle(New Point(0, 0), New Size(me.size.width, 250)) ' oberer Schatten
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    M-Stef schrieb:

    das habe ich ja geschrieben
    Eben nicht.
    Du invalidisierst eine große Fläche, das meiste davon unbegründet.
    Ich invalidisiere eben genau die Teile, die tatsächlich neu gezeichnet werden müssen.
    Berechne mal die je invalidisierten Flächen (Pixel-Anzahlen).
    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!
    Ahh sorry, habs jetzt glaube ich endlich kapiert.

    Du meinst das so: stepbystep.png
    Bilder
    • stepbystep.PNG

      113,6 kB, 1.209×721, 168 mal angesehen
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    M-Stef schrieb:

    Obschon me.doublebuffered auf True ist!
    Nur nochmal kurz als Zwischenruf, normalerweise bekommt man es immer hin die Form ohne Flackern zeichnen zu lassen. Ja manchmal ist das etwas Schwierig und man muss ein bisschen rumprobieren, aber immer möglich. Wenn ich z.B. auf einer Form nur selber Zeichne setz ich meist im Konstruktor sowas wie

    VB.NET-Quellcode

    1. ​SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.Opaque Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint, True)
    *entfernt*
    SetStyle funzt leider auch nicht. Es ist weniger der Konstruktor, sondern der Aktualisierungsintervall von 10ms. Die Grafikkarte kommt glaube ich einfach nicht hinterher.

    ~blaze~: Vollzitat entfernt
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

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

    Jain. Das Problem ist, dass im Resize Event nicht nur die Formgrösse geändert wird, sondern die Schatten im Hintergrund gleichzeitig neu gezeichnet werden.
    Wenn ich die Schatten weglassen würde, würde überhaupt nichts mehr flackern.

    Aber ich will meine Schatten haben. :)

    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop
    Das dachte ich auch eine ganze Zeit lang, bis ich letztens das gegenteil auf MSDN gelesen habe. GDI/GDI+ ist natürlich langsamer, weil das alles komisch gewrappt ist, aber seit Vista läuft alles auf DirectX hinaus: [Existing graphics interfaces, such as GDI, GDI+, and older versions of Direct3D, continue to work on Windows Vista and Windows 7, but are internally remapped where possible.]

    Quelle: Graphics APIs in Windows
    @Eddy

    Seit wann ist

    VB.NET-Quellcode

    1. ​e.Graphics.DrawImage(...)
    GDI? Das ist doch GDI+? Spielt ja auch keine Rolle, ist beides langsam.
    Sei es wies sei, WPF ist keine Alternative für mich. Macht überhaupt kein Spass zum proggen mit WPF.
    Normale Windows Form Anwendungen kann man viel effizienter erstellen.

    Egal, ich schliesse diesen Thread nun. Macht keinen Sinn nach Lösungen zu suchen, wenn es keine möglichen Lösung gibt.

    Trotzdem vielen Dank euch allen für eure Tipps! :thumbup:
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop
    @Bluespide

    Ja klar, wenn du möchtest kannste gerne mal reinschauen. Falls dir alles verzieht könnte es sein, dass du die benötigte Schriftart nicht installiert hast. Damit alles wie bei mir ist, musst du einfach mindestens Roboto-Light.ttf(Google Schriftart) installieren (habe ich beigelegt).

    Wenn du oder ein anderer von euch doch noch auf die Lösung kommt, wäre ich froh, wenn ihr mir das schreiben würdet.
    Das Hauptproblem befindet sich beim main_window.

    Danke! :thumbup:
    Dateien
    • Prey_UI_3.zip

      (3,62 MB, 149 mal heruntergeladen, zuletzt: )
    "Die menschliche Vorstellungskraft ist unendlich"
    ->Versuch dir mal 'ne neue Farbe auszudenken!

    Mit Schleifen kann man alles lösen!

    Dim d as Double = 1
    Do until d = 0
    d = (d / 2)
    Loop

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „M-Stef“ ()

    Ah ja, ich sehe schon. Das ist eine Kombination aus viel selber Zeichnen und einigen Controls. Ich hab das Flackern jetzt bei mir so gut wie ganz weg bekommen. Ich versuch das mal so gut es geht zu erklären und dabei ein paar "Regeln/Hilfen" zu nennen (ich bin aber nicht so gut darin ^^ ).

    Also das SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint, True) gehört nicht in das main_window_Paint Event, sondern in den Konstruktor von dem Control, dass gezeichnet wird. Also einmal der Form:

    VB.NET-Quellcode

    1. Private Sub main_window_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. Me.MaximumSize = New Size(My.Computer.Screen.WorkingArea.Width, My.Computer.Screen.WorkingArea.Height)
    3. Me.DoubleBuffered = True
    4. Call form_shadow.getClassLong(Me.Handle.ToInt32, form_shadow.GCL_STYLE, form_shadow.SetClassLong(Me.Handle.ToInt32, form_shadow.GCL_STYLE) Or form_shadow.CS_DROPSHADOW) 'formschatten
    5. createfolder()
    6. getmails()
    7. 'Hier
    8. SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer Or ControlStyles.UserPaint, True)
    9. End Sub

    und dann eigentlich noch in die Controls bei denen du selber auch was Zeichnest. Da das hier bei dir viele sind, habe ich das einfach mal auf die schnelle pauschal hier bei allen gemacht. Also erst musst du dir Basis Controls erstellen, die den Style setzen:

    VB.NET-Quellcode

    1. Public Class MyPanel
    2. Inherits Panel
    3. Public Sub New()
    4. Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True)
    5. End Sub
    6. End Class
    7. Public Class MyPictureBox
    8. Inherits PictureBox
    9. Public Sub New()
    10. Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True)
    11. End Sub
    12. End Class
    13. Public Class MyButton
    14. Inherits Button
    15. Public Sub New()
    16. Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True)
    17. End Sub
    18. End Class
    19. Public Class MyLabel
    20. Inherits Label
    21. Public Sub New()
    22. Me.SetStyle(ControlStyles.UserPaint Or ControlStyles.AllPaintingInWmPaint Or ControlStyles.OptimizedDoubleBuffer, True)
    23. End Sub
    24. End Class

    und danach in der main_window.Designer.vb diese entsprechend benutzen

    VB.NET-Quellcode

    1. Private Sub InitializeComponent()
    2. Me.components = New System.ComponentModel.Container()
    3. Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(main_window))
    4. Me.Panel6 = New MyPanel()
    5. Me.maxmin_control = New MyPanel()
    6. Me.Button12 = New MyButton()
    7. Me.Button10 = New MyButton()
    8. Me.Button11 = New MyButton()
    9. Me.Button9 = New MyButton()
    10. Me.Label1 = New MyLabel()
    11. Me.Timer1 = New System.Windows.Forms.Timer(Me.components)
    12. Me.main_container = New MyPanel()
    13. Me.Label5 = New System.Windows.Forms.Label()
    14. Me.PictureBox7 = New MyPictureBox()
    15. Me.PictureBox6 = New MyPictureBox()
    16. Me.PictureBox1 = New MyPictureBox()
    17. Me.Label4 = New MyLabel()
    18. Me.Label3 = New MyLabel()
    19. Me.Label2 = New MyLabel()
    20. Me.Button3 = New MyButton()
    21. Me.Button2 = New MyButton()
    22. Me.Button1 = New MyButton()
    23. Me.PictureBox4 = New MyPictureBox()
    24. Me.PictureBox3 = New MyPictureBox()
    25. Me.PictureBox5 = New MyPictureBox()
    26. Me.PictureBox2 = New MyPictureBox()
    27. Me.Panel6.SuspendLayout()
    28. Me.maxmin_control.SuspendLayout()
    29. Me.main_container.SuspendLayout()
    30. CType(Me.PictureBox7, System.ComponentModel.ISupportInitialize).BeginInit()
    31. CType(Me.PictureBox6, System.ComponentModel.ISupportInitialize).BeginInit()
    32. CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).BeginInit()
    33. CType(Me.PictureBox4, System.ComponentModel.ISupportInitialize).BeginInit()
    34. CType(Me.PictureBox3, System.ComponentModel.ISupportInitialize).BeginInit()
    35. CType(Me.PictureBox5, System.ComponentModel.ISupportInitialize).BeginInit()
    36. CType(Me.PictureBox2, System.ComponentModel.ISupportInitialize).BeginInit()
    37. Me.SuspendLayout()


    Gut, damit sollte das Flackern schon mal weg sein, aber deine Performance in der main_window_Paint ist nicht so toll. Am besten diese noch steigern. Im Moment sieht die Methode so aus:

    VB.NET-Quellcode

    1. Private Sub main_window_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    2. e.Graphics.DrawImage(My.Resources.shadow01, -250, 0, 500, Me.Height)
    3. e.Graphics.DrawImage(My.Resources.shadow02, Me.Width - 250, 0, 500, Me.Height)
    4. e.Graphics.DrawImage(My.Resources.shadow03, 0, -250, Me.Width, 500)
    5. e.Graphics.DrawImage(My.Resources.shadow04, 0, Me.Height - 250, Me.Width, 500)
    6. End Sub

    Das Problem dabei ist das My.Resources.shadow01. Ein Bitmap direkt aus den Resources zu zeichnen ist meeeega langsam und sollte niemals gemacht werden, es sei denn Performance ist irrelevant. Also am besten die Bitmaps einmal kopieren, so:

    VB.NET-Quellcode

    1. Private shadow01 As Bitmap = New Bitmap(My.Resources.shadow01)
    2. Private shadow02 As Bitmap = New Bitmap(My.Resources.shadow02)
    3. Private shadow03 As Bitmap = New Bitmap(My.Resources.shadow03)
    4. Private shadow04 As Bitmap = New Bitmap(My.Resources.shadow04)
    5. Private Sub main_window_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
    6. e.Graphics.DrawImage(Me.shadow01, -250, 0, 500, Me.Height)
    7. e.Graphics.DrawImage(Me.shadow02, Me.Width - 250, 0, 500, Me.Height)
    8. e.Graphics.DrawImage(Me.shadow03, 0, -250, Me.Width, 500)
    9. e.Graphics.DrawImage(Me.shadow04, 0, Me.Height - 250, Me.Width, 500)
    10. End Sub

    Desweiteren ist es sehr kostspielig Bitmaps in einer anderen Größe zu Zeichnen. Generell sind kleinere Bilder natürlich schneller. Hier ist Beispielsweise shadow01 640 Pixel breit, aber du Zeichnest es mit einer Breite von 500 und außerdem 250 Pixel ins Nichts verschoben. Am besten versuchen die Bilder direkt in einer entsprechenden Größe in die Resourcen zu packen oder einmal beim laden in die Variable anpassen.

    Ich hoffe das hilf dir schon mal und du weißt damit was anzufangen. Natürlich kann man da noch weiter optimieren, aber für den Anfang sollte das schon mal reichen. Ich hab das angepasste Projekt mit angehängt.
    Dateien