Automtisch zeichnen nach dem Laden

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

Es gibt 26 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Automtisch zeichnen nach dem Laden

    Hallo,

    bestimmt ist das eine ganz einfache Sache: ich habe eine Uhr programmiert und möchte, direkt nach dem Start des Programms diese Uhr auf eine PicBox zeichnen. Bei FormLoad wird nix gezeichnet, also habe ich es bei Form-Activate probiert oder bei Form-Shown - geht alles nicht. Was hab ich nicht bedacht oder übersehen?

    PB

    VB.NET-Quellcode

    1. Private Sub Form1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
    2. For min = 1 To 60
    3. x = xpos(min, (rs + 2))
    4. y = rs * Math.Sin(Math.PI / 2 - Math.PI / 30 * min)
    5. ptmin.X = x + mitte.X
    6. ptmin.Y = mitte.Y - y
    7. g.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    8. Next
    9. End Sub


    also, wenn ich das per Mausklick ausführe, klappt das - nur nicht "selbständig" nach dem Laden...
    habe da was Merkwüridges:

    VB.NET-Quellcode

    1. Dim mitte As Point = New Point(50, 50)


    laut dieser Festlegung müsste doch mitte.x=50 und mitte .y = 50 sein? Nach deinem vorgeschlagenen Check, steht da aber jeweils 100 ??!! Also mitte.x=100 - und damit bin ich außerhalb meiner Zeichenfläche...
    ok - das habe ich gefunden - loest aber nicht das Problem

    Ich würde ja mal den ganzen Code posten - wenn das nicht zu viel Mühe macht, sich da rein zu denken...?!

    Das Programm besteht nur aus einer Picturebox, 4Buttons und einem Timer

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim ts, tm, th, x, y As Double
    3. 'Radien bzw. Zeigerlänge von SEk(rs) und Min (rm) und Stunde (rh)
    4. Dim rs, rm, rh As Double
    5. Dim g As Graphics
    6. Dim mitte As Point = New Point(50, 50)
    7. Dim ptsec, ptmin, pth As Point
    8. 'Farben&Stifte
    9. Dim hfarbe As Color
    10. Dim schwstiftsec As Pen = New Pen(Brushes.Black, 2)
    11. Dim hstiftsec As Pen = New Pen(hfarbe, 2)
    12. Dim schwstiftmin As Pen = New Pen(Brushes.Black, 2)
    13. Dim hstiftmin As Pen = New Pen(hfarbe, 2)
    14. Dim schwstifthour As Pen = New Pen(Brushes.Black, 2)
    15. Dim hstifthour As Pen = New Pen(hfarbe, 2)
    16. Private Sub Form1_Activated(sender As Object, e As EventArgs) Handles Me.Activated
    17. For min = 1 To 60
    18. x = xpos(min, (rs + 2))
    19. y = rs * Math.Sin(Math.PI / 2 - Math.PI / 30 * min)
    20. ptmin.X = x + mitte.X
    21. ptmin.Y = mitte.Y - y
    22. g.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    23. Next
    24. End Sub
    25. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    26. Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
    27. ' Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)
    28. 'Me.SetStyle(ControlStyles.UserPaint, True)
    29. 'Mittelpunkt der Uhr
    30. mitte.X = PictureBox1.Width \ 2
    31. mitte.Y = PictureBox1.Height \ 2
    32. 'Zeigerlänge
    33. rs = PictureBox1.Height \ 2 - 11
    34. rm = rs
    35. rh = rs * 2 \ 3
    36. hfarbe = PictureBox1.BackColor
    37. hstiftmin.Color = PictureBox1.BackColor
    38. hstiftsec.Color = PictureBox1.BackColor
    39. g = PictureBox1.CreateGraphics
    40. ' gmin = PictureBox1.CreateGraphics
    41. End Sub
    42. Private Sub minutenpunkte()
    43. For min = 1 To 60
    44. x = xpos(min, (rs + 2))
    45. y = rs * Math.Sin(Math.PI / 2 - Math.PI / 30 * min)
    46. ptmin.X = x + mitte.X
    47. ptmin.Y = mitte.Y - y
    48. g.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    49. Next
    50. End Sub
    51. Private Sub btnsek_Click(sender As Object, e As EventArgs) Handles btnsek.Click
    52. TimerSec.Enabled = True
    53. End Sub
    54. Private Sub btnMin_Click(sender As Object, e As EventArgs) Handles btnMin.Click
    55. 'TimerMin.Enabled = True
    56. End Sub
    57. Private Sub btnMinPunkte_Click(sender As Object, e As EventArgs) Handles btnMinPunkte.Click
    58. minutenpunkte()
    59. End Sub
    60. Function xpos(t As Integer, r As Integer)
    61. xpos = r * Math.Cos(Math.PI / 2 - Math.PI / 30 * t)
    62. End Function
    63. Function ypos(t As Integer, r As Integer)
    64. ypos = r * Math.Sin(Math.PI / 2 - Math.PI / 30 * t)
    65. End Function
    66. Private Sub TimerSec_Tick(sender As Object, e As EventArgs) Handles TimerSec.Tick
    67. 'Sekundenzeiger
    68. g.DrawLine(hstiftsec, mitte, ptsec)
    69. ts = Second(Now)
    70. ptsec.X = mitte.X + xpos(ts, rs)
    71. ptsec.Y = mitte.Y - ypos(ts, rs)
    72. g.DrawLine(schwstiftsec, mitte, ptsec)
    73. 'Minutenzeiger
    74. g.DrawLine(hstiftmin, mitte, ptmin)
    75. tm = Minute(Now)
    76. ptmin.X = mitte.X + xpos(tm, rm)
    77. ptmin.Y = mitte.Y - ypos(tm, rm)
    78. g.DrawLine(schwstiftmin, mitte, ptmin)
    79. 'Stundenzeiger
    80. g.DrawLine(hstifthour, mitte, pth)
    81. th = Hour(Now)
    82. If th < 13 Then th = th * 5 Else th = (th - 12) * 5
    83. pth.X = mitte.X + xpos(th, rh)
    84. pth.Y = mitte.Y - ypos(th, rh)
    85. g.DrawLine(schwstiftmin, mitte, pth)
    86. TimerSec.Interval = 1000
    87. End Sub
    88. End Class

    Ich würde dir empfehlen den Code zum Zeichnen in das entsprechende Paint-Event zu legen, dort die mitgegebene Graphics nutzen und dann einfach immer Invalidate oder Refresh aufzurufen. Das ist von der Struktur her besser.
    Außerdem: Stelle unbedingt Option Strict ON! Funktionen müssen immer einen Rückgabewert haben oder aber als Sub deklariert werden!
    Als nächstes würde ich den Code aus dem Activated-Event in die Load-Prozedur stecken und bei eben dieser Prozedur das abonnierte Event zu Form.Shown ändern.
    Wenn du das alles gemacht hast und die Fehler von Option Strict Off behoben hast, dann hau nen Breakpoint in das Shown-Event und steppe durch und achte genau auf die Werte, wenn etwas vom Erwartungswert abweicht hast du deinen Fehler gefunden.

    LG

    PS: Nutze die Date-Klasse und nicht diese alten VB6-Funktionen. Die Date-Klasse ist deutlich mächtiger und schöner.
    erst mal vielen Dank für die vielen Tipps -

    also ich habe schon vor längerem den Strict on tipp bekommen und es auch so eingestellt - (Menü-Extras..usw.) - kann es sein, dass das nicht funktioniert, wenn ich ein altes Prjekt aufrufe und dann daran weiterarbeite, dass diese Voreinstellung dann nicht greift?
    "Code zum Zeichnen in das entsprechende Paint-Event zu legen, dort die mitgegebene Graphics nutzen"
    das verstehe ich nicht ganz - was soll ich da genau machen?

    also: die Funktionen hab ich geändert:

    VB.NET-Quellcode

    1. Function xpos(t As Integer, r As Integer) As Integer
    2. xpos = CInt(r * Math.Cos(Math.PI / 2 - Math.PI / 30 * t))
    3. End Function


    und auch die ganzen Variablen angepasst, weil Strict keine Implizite Konvertierung zulässt

    aber wie ist das mit dem paint-Event - das weiß ich leider nicht

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

    Nun jedes Control besitzt ein Paint-Event, welches im Parameter eine EventArgs mit einer Graphics mitbringt. Diese Graphics ist auf das entsprechende Control bezogen, sprich man zeichnet in das Koordinatensystem des Controls(also wäre Punkt 1/1 von der Form aus gesehen nicht bei 1/1, sondern eben um die Position des Controls verschoben). Du abonnierst nun einfach das Paint-Event deiner Picturebox und verwirfst dein Graphics-Attribut, also du zeichnest nur noch im Paint-Event. Im Timer bspw kannst du dann die Invalidate-Methode deiner Picturebox aufrufen und es wird immer das Paint-Event ausgeführt bzw die Graphics der Picturebox wird geleert und neu gezeichnet.

    Auch wichtig: Nutze bei Funktionen die Return-Anweisung und setze nicht so unsauber den Funktionswert. Wegen Option Strict On musst du wsl noch in deinen Projekteigenschaften nochmal ändern, es kann sein, dass VS das nur für zukünftige Projekte ändert.

    LG
    Bin nun schon einen Schritt weiter:
    1. habe deinen Rat bezüglich "Return" umgesetzt
    2. Option Strict On ist zwar voreingestellt, wird aber auch beim Anlegen eines neuen Projektes nicht automatisch gesetzt - mach ich halt selbst
    3. Meine 60 Punkte für die Minuten werden nun im Paint-Event gezeichnet - allerdings kann ich dann kein eigenes Private Sub mehr nutzen, wie das ursprünglich hatte, bzw. ich weiß nicht wie, da e.Graphics ... ja nur innerhalb der Picturebox abrufbar ist.
    4. Meine Uhrzeiger laufen auch - auch da würde ich der Übersicht halber gerne private subs nutzen - geht das ?


    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. 'Koordinatenvariablen
    4. Dim x, y As Integer
    5. 'Zeitvariablen für sec,min,stunde
    6. Dim ts, tm, th As Integer
    7. 'Radien bzw. Zeigerlänge von SEk(rs) und Min (rm) und Stunde (rh)
    8. Dim rs, rm, rh As Double
    9. Dim mitte As Point 'Bildmitte der Picbox
    10. Dim ptsec, ptmin, pth As Point ' Endpunkte der drei Zeiger
    11. 'Farben&Stifte
    12. Dim hfarbe As Color
    13. Dim schwstiftsec As Pen = New Pen(Brushes.Black, 2)
    14. Dim hstiftsec As Pen = New Pen(hfarbe, 2)
    15. Dim schwstiftmin As Pen = New Pen(Brushes.Black, 2)
    16. Dim hstiftmin As Pen = New Pen(hfarbe, 2)
    17. Dim schwstifthour As Pen = New Pen(Brushes.Black, 2)
    18. Dim hstifthour As Pen = New Pen(hfarbe, 2)
    19. Function xpos(t As Integer, r As Double) As Integer
    20. Return CInt(r * Math.Cos(Math.PI / 2 - Math.PI / 30 * t))
    21. End Function
    22. Function ypos(t As Integer, r As Double) As Integer
    23. Return CInt(r * Math.Sin(Math.PI / 2 - Math.PI / 30 * t))
    24. End Function
    25. Private Sub minutenpunkte()
    26. For min = 1 To 60
    27. x = xpos(min, (rs + 2))
    28. y = ypos(min, (rs + 2))
    29. ptmin.X = x + mitte.X
    30. ptmin.Y = mitte.Y - y
    31. ' g.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    32. Next
    33. End Sub
    34. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    35. For min = 1 To 60
    36. x = xpos(min, (rs + 2))
    37. y = ypos(min, (rs + 2))
    38. ptmin.X = x + mitte.X
    39. ptmin.Y = mitte.Y - y
    40. e.Graphics.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    41. Next
    42. 'Sekundenzeiger
    43. e.Graphics.DrawLine(hstiftsec, mitte, ptsec)
    44. ts = Second(Now)
    45. ptsec.X = mitte.X + xpos(CInt(ts), CInt(rs))
    46. ptsec.Y = mitte.Y - ypos(CInt(ts), CInt(rs))
    47. e.Graphics.DrawLine(schwstiftsec, mitte, ptsec)
    48. End Sub
    49. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    50. End Sub
    51. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    52. 'Mittelpunkt der Uhr
    53. mitte.X = PictureBox1.Width \ 2
    54. mitte.Y = PictureBox1.Height \ 2
    55. 'Zeigerlänge
    56. rs = PictureBox1.Height \ 2 - 11
    57. rm = rs
    58. rh = CLng(rs * 2) \ 3
    59. hfarbe = PictureBox1.BackColor
    60. hstiftmin.Color = PictureBox1.BackColor
    61. hstiftsec.Color = PictureBox1.BackColor
    62. End Sub
    63. Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    64. PictureBox1.Invalidate()
    65. End Sub
    66. End Class

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

    PowerBauer schrieb:

    3. Meine 60 Punkte für die Minuten werden nun im Paint-Event gezeichnet - allerdings kann ich dann kein eigenes Private Sub mehr nutzen, wie das ursprünglich hatte, bzw. ich weiß nicht wie, da e.Graphics ... ja nur innerhalb der Picturebox abrufbar ist.
    4. Meine Uhrzeiger laufen auch - auch da würde ich der Übersicht halber gerne private subs nutzen - geht das ?

    Könntest du das bitte nochmal verständlich schreiben? Ich habe gerade ein Problem damit deinen Fragen zu folgen, denn "Private Sub" haben erstmal nichts mit Graphics, Pictureboxen oder Uhrzeigern zu tun. Subs sind Methoden bzw Funktionen ohne Rückgabewert und "Private" ist nur ein Schlüsselwort für die Sichtbarkeit wie eben Public, Protected, etc....
    Erläutere mal genau was dein Problem ist und versuche die Erläuterung möglichst einfach zu halten.
    Btw hast du immernoch das VB6-Ranzzeug drin.
    Nutze einfach die Date-Klasse(bzw DateTime-Klasse, is des selbe), die hat entsprechende Properties:

    VB.NET-Quellcode

    1. ​Dim x As Date = Date.Now
    2. Dim sec = x.Second
    3. Dim min = x.Minute
    4. Dim hour = x.Hour


    LG
    ok - danke - werde also versuchen das "Ranzzeug" :P zu eliminieren. Ich habe halt immer mit VB5 und VB6 programmiert - die Umstellung dauert etwas. Und leider findet man nicht oft geduldige Erklärer....

    Also meine Frage bezieht sich auf die Gestaltung des Programms. Statt folgendem Code:

    VB.NET-Quellcode

    1. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    2. 'MinutenPunkte
    3. For min = 1 To 60
    4. x = xpos(min, (rs + 3))
    5. y = ypos(min, (rs + 3))
    6. ptmin.X = x + mitte.X
    7. ptmin.Y = mitte.Y - y
    8. If min / 5 = min \ 5 Then
    9. e.Graphics.FillEllipse(Brushes.SeaGreen, ptmin.X, ptmin.Y, 4, 4)
    10. Else
    11. e.Graphics.DrawEllipse(Pens.AliceBlue, ptmin.X, ptmin.Y, 2, 2)
    12. End If
    13. Next
    14. End Sub


    würde ich gerne so etwas wie:

    VB.NET-Quellcode

    1. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    2. MinutenPunkte() '= ein extra geschriebenes Unterprogramm
    3. End Sub


    nutzen.
    Ist das verständlicher?
    Achso, jetzt weiß ich was du meinst.
    Naja lustigerweise hätte wollte ich dir das sowieso noch raten, nämlich die einzelnen Codestücke in Methoden zu kapseln, damit du es leichter warten kannst und du Fehler schneller findest.
    Das ist natürlich kein Problem und auch empfehlenswert. Du schreibst dann einfach Methoden mit einer Graphics als Parameter, mit der du dann zeichnest.
    Also bspw:

    VB.NET-Quellcode

    1. ​Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    2. zeichneMinuten(e.Graphics)
    3. zeichneStundenZeiger(e.Graphics)
    4. End Sub
    5. Private Sub zeichneMinuten(e As Graphics)
    6. With e
    7. .DrawRectangle(...)
    8. 'bla
    9. End With
    10. End Sub
    11. Private Sub zeichneStundenZeiger(e As Graphics)
    12. With e
    13. .DrawRectangle(...)
    14. 'bla
    15. End With
    16. End Sub


    Alternativen wären Events oder sogar ein eigenes Control zu schreiben. Das wäre wahrscheinlich die eleganteste Lösung, wenn du einfach ein eigenes Control schreibst(kann man direkt von Control erben lassen oder von bspw einem Panel) und dort das per Events löst.

    LG
    sieht aus, als wollest du eine ownerdrawn Uhr proggen.

    hab ich auch mal gemacht, da gibts einiges, was einem dabei helfen kann.

    such mal auf dieser Site: activevb.de/cgi-bin/upload/upload.pl
    Nach
    "clocksample"

    Die Crux (eine von mehreren) ist, eine Extra-Klasse zu schaffen, die die Zeichnung jederzeit reproduzieren kann, unabhängig von irgendeinem Timer.
    jetzt muss ich aber mal ein ganz dickes Lob aussprechen - so viel Hilfe - einfach klasse!
    Hab mit meinem kleinen Uhr-Projekt schon eine ganze Menge gelernt - vielen Dank an alle, die bisher mitgeholfen haben!

    Eins stört mich noch - und das wir wohl nicht einfach zu lösen sein: Meine Uhrzeiger sind optisch nicht gerade schön, wenn die Linien wenigstens schön glatt wären - aber so treppenstufig...
    ich könnte die Zeit nur alle Viertelstunde anzeigen lassen -dann sind die Zeiger in Ordnung :P

    gibts da was, oder muss man da gaaaaaaanz tief in die Trickkiste greifen?

    PowerBauer schrieb:

    Meine Uhrzeiger sind optisch nicht gerade schön, wenn die Linien wenigstens schön glatt wären

    Sieh dir mal die Properties der Graphics-Klasse an(MSDN oder im Objectbrowser), an denen kannst du dich mal ausprobieren.
    Aber ich gebe dem EDR recht, das ganze wäre mit Ownerdrawing natürlich eleganter. Halte ich aber erstmal für "Mit Kanonen auf Spatzen schießen".

    LG