Zeichnen bei Mausklick (GDI Anfänger)

  • VB.NET

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

    Zeichnen bei Mausklick (GDI Anfänger)

    Hallo Forum Experten,

    Es soll ein Kreis gezeichnet werden, wenn die Maustaste gedrückt wird.
    Hier mein Code:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim p As Pen = New Pen(Color.White, 1)
    3. Dim b As Brush = Brushes.Goldenrod
    4. Dim x, y As Integer
    5. Dim eHeight As Integer = 50
    6. Dim eWidth As Integer = 50
    7. Dim bmp As New Bitmap(600, 400)
    8. Dim gr As Graphics = Graphics.FromImage(bmp)
    9. Private Sub PictureBox1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseClick
    10. Dim pt_client As New Point
    11. pt_client = PictureBox1.PointToClient(Control.MousePosition)
    12. x = pt_client.X
    13. y = pt_client.Y
    14. NewEllipse()
    15. End Sub
    16. Private Sub PictureBox1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    17. gr.FillEllipse(b, x, y, eWidth, eHeight)
    18. gr.DrawEllipse(p, x, y, eWidth, eHeight)
    19. End Sub
    20. Private Sub NewEllipse()
    21. PictureBox1.Image = bmp
    22. End Sub
    23. End Class


    Der Kreis wird erst nach dem zweiten Mausklick an der letzten Stelle gezeichnet ?( wo sich die Maus befand.
    Iwie hab ich gerade Gardienen vor den Augen und finde nicht das Problem. Könnte mir bitte jemand einen Tipp geben?
    Eine Zusatzfrage hätte ich noch, ich hab gelesen man sollte lieber einen Panelcontrol statt PictureBox nehmen. Ist da was dran?
    (Mit einem Panel hab ich es garnicht hinbekommen.)

    Danke im vorraus.
    Die Picturebox hat ein eigenes Graphics Object welches in e bei PictureBox1_Paint drinne steckt (dann brauchst du kein extra bitmap/graphics object erstellen)
    Auserdem musst du Picturebox1.Invalidate() aufrufen damit sich die Picturebox neuzeichnet.

    Guck mal das:

    VB.NET-Quellcode

    1. Private _body As Rectangle
    2. Private Sub PictureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDown
    3. If e.Button = Windows.Forms.MouseButtons.Left Then
    4. Dim width As Integer = 25
    5. Dim height As Integer = 25
    6. Dim location As Point = New Point(e.Location.X - width \ 2, e.Location.Y - height \ 2)
    7. _body = New Rectangle(location, New Size(width, height))
    8. Else
    9. Return
    10. End If
    11. PictureBox1.Invalidate()
    12. End Sub
    13. Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
    14. e.Graphics.FillEllipse(Brushes.Red, _body)
    15. End Sub

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

    @newbie Falls die schon gezeichneten Kreise stehen bleiben sollen, wenn Du an eine neue Stelle klickst, dann musst Du Dir die Mittelpunkte / Radien / Farben der bisherigen Kreise in einer List(Of Point) oder List(Of MyCircle) merken und om Paint-Event alle neu zeichnen.
    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!
    danke für eure Antworten,

    so funktioniert es, und ich habe es sogar halbwegs verstanden. ;)

    @RodFromGermany List(Of Point) ist ne Super Idee, aber ich werde die gesetzten Kreise (Koordinaten) in eine DB speichern und von dort wieder abholen.

    Schönen Sonntag noch..

    ErfinderDesRades schrieb:

    ist reichlich kompliziert genug
    Jou.
    @newbie Zerlege Dein Problem in disjunkte Teilprobleme:
    - Malen eines Kreises
    - Malen vieler Kreise nacheinander
    - Verschieben eines Kreises
    - Verschieben einer Gruppe von Kreisen
    - Speichern von Daten
    - ...
    Löse diese Probleme in je einem kleinen Projekt, zu dem Du dann je einen Thread hier aufmachen kannst.
    Wenn Du dann alle Deine Probleme zu Deiner Zufriedenheit gelöst hast, füge den Code in einem weiteren Projekt zusammen. :thumbsup:
    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!

    newbie schrieb:

    Wie geht sowas?
    Im MouseDown-Event musst Du feststellen, ob und wenn ja in welchem Deiner Objekte Du hineingeklickt hast. Wenn ja, folgt dieses Objekt der Mausbewegung.
    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!
    Wenn du den Kram speichern und wieder laden willst, dann fängst du grad falsch an.
    Du musst mit den Daten anfangen, wie gesagt: Schaff dir ein typisiertes Dataset, mit einer Tabelle, wo deine Kreise drinne sind, mit X, Y. Width, Height, vlt auch mit Color, Strichstärke und whatever.
    Dann eine Primitiv-Oberfläche machen, wo du per Datagridview ein paar Test-Kreise eingeben kannst und speichern.
    Erst dann kannst du mit Code anfangen, der das Zeug auf die Oberfläche malt, und mitte Maus interagiert.
    Zur Datenseite gugge
    die vier Views auf Video und weiterführende Links

    hmm - wird noch komplizierter, denn du musst in partialen DataRow-Klassen die Business-Logik einbauen.

    Also die Busyness-Logik hab ich hier, aber das ist leider nicht mit einem typDataset integriert:
    StoryCards - verschiebbare Zeichenobjekte
    Hi,
    danke für die Antworten.

    Das nacheinander Zeichnen per Mausklick funktioniert wunderbar. Die Koordinaten werden wie folgt temporär zur Laufzeit gespeichert.

    VB.NET-Quellcode

    1. Dim PunkteListe As New List(Of Point)


    Als
    nächstes suche ich die gezeichneten Shapes. Es soll nur ganz simple bei
    Mausbewegung die gezeichneten Shapes finden und zur Veranschaulichung
    meinen "btnSignal" Grün schalten.

    VB.NET-Quellcode

    1. Private Sub Panel1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove
    2. 'Es wird vorher bestimmt ob ich neu zeichnen, bearbeiten (bewegen) oder löschen möchten....
    3. 'Beim FormLoad: Dim shapeNew, shapeMove, shapeDel as Boolean deklariert.
    4. If shapeMove = True Then
    5. Dim LocationProzent As Point
    6. LocationProzent.X = (100 / Panel1.Width) * e.Location.X
    7. LocationProzent.Y = (100 / Panel1.Height) * e.Location.Y
    8. koord = LocationProzent
    9. Label7.Text = LocationProzent.X 'Anzeigen der Position in %
    10. Label8.Text = LocationProzent.Y 'Anzeigen der Position in %
    11. If find_shape(koord) = True Then
    12. btnSignal.BackColor = Color.Green
    13. Else
    14. btnSignal.BackColor = Nothing
    15. End If
    16. End If
    17. End Sub
    18. Private Function find_shape(ByVal myPos As Point) As Boolean
    19. Dim x_offset, y_offset As Integer
    20. x_offset = 5 'Um einen Bereich festzulegen von bis...wird später noch eingebunden
    21. y_offset = 5 'Um einen Bereich festzulegen von bis...wird später noch eingebunden
    22. For Each Shapepunkte As Point In PunkteListe
    23. If myPos.X = Shapepunkte.X And myPos.Y = Shapepunkte.Y Then
    24. Return True
    25. Else
    26. Return False
    27. End If
    28. Next
    29. Return Nothing
    30. End Function


    Es findet NUR den ersten Eintrag (egal wo es gezeichnet ist) obwohl z.B. mehrere in der List(Of Point) stehen.
    Ich komme nicht drauf, die For Each Schleife läuft doch durch...oder?

    Vielen Dank für die Hilfe im vorraus.

    MfG
    newbie

    newbie schrieb:

    den ersten Eintrag
    Wie befüllst Du Deine PunkteListe?
    Die Prozentrechnung solltest Du weglassen, nimm die echten Koordinaten.
    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:

    Wie befüllst Du Deine PunkteListe?


    VB.NET-Quellcode

    1. Private Sub Panel1_MouseClick(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseClick
    2. If shapeNew = True Then
    3. If e.Button = Windows.Forms.MouseButtons.Left Then
    4. Dim myInput As String
    5. myInput = InputBox("Sensor?", "BMK")
    6. If myInput = "" Then
    7. Exit Sub
    8. Else
    9. Dim LocationProzent As Point
    10. LocationProzent.X = (100 / Panel1.Width) * e.Location.X
    11. LocationProzent.Y = (100 / Panel1.Height) * e.Location.Y
    12. PunkteListe.Add(LocationProzent)
    13. bmkListe.Add(myInput)
    14. End If
    15. Else
    16. Return
    17. End If
    18. Panel1.Invalidate()
    19. End If
    20. End Sub


    Die Prozentrechnung solltest Du weglassen, nimm die echten Koordinaten.

    Das ist eigentlich egal, ich brauche die Koordinaten aber in %, weil ich mit relativen Größen arbeiten möchte.

    Ich lasse mir die zur Kontrolle anzeigen.

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    2. TextBox1.Clear()
    3. Dim i As Integer = 0
    4. For Each bmkstring As String In bmkListe
    5. TextBox1.Text = TextBox1.Text & bmkstring & PunkteListe(i).ToString & ", " & Chr(9) 'Chr(9) = Tab
    6. i += 1
    7. Next
    8. End Sub

    S1{X=16,Y=48}, S2{X=57,Y=39}, S3{X=85,Y=67},
    @newbie OK.
    Fang an und gib dem Einfangbereich die Toleranz. Hier brauchst Du allerdings Pixel, denn die Maus weiß nix von Deinen Prozenten.
    Teste ab, ob der Punkt innerhalb eines Kreises um den Zielpunkt liegt.
    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:

    Fang an und gib dem Einfangbereich die Toleranz. Hier brauchst Du allerdings Pixel, denn die Maus weiß nix von Deinen Prozenten.

    Ja das ist richtig, das werde ich natürlich dann berücksichtigen.

    Mir geht es in erster Linie darum, warum meine "btnSignal" nicht bei allen drei Shapes nicht grün wird. Sondern nur bei dem ersten.
    Ich lade mal einige Screens hoch, dann wir es verständlicher?

    Kurze Erläuterung der Bilder:
    1. Start : Programmstart, einpaar hilfslabels mit Bildschirmgröße, Panelgröße, Mausposition usw.
    2. Neu : Button New wurde gedrückt und innerhalb des Panels wurde mit linker Maustaste geklickt.
    3. Sensoren : Nun wurden die Shapes plaziert.
    4. Kontrolle : Button1 und Move wurde gedrükt und die Maus steht mittig über S1, btnSignal (oben rechts) wird grün "Shape gefunden"
    5. KontrolleAus : Bei S2, S3 und S4 bleibt btnSignal aus.
    Bilder
    • Start.JPG

      41,36 kB, 855×586, 174 mal angesehen
    • Neu.JPG

      53,65 kB, 905×604, 151 mal angesehen
    • Sensoren.JPG

      61,45 kB, 1.168×776, 137 mal angesehen
    • Kontrolle.jpg

      154,32 kB, 1.168×776, 138 mal angesehen
    • KotrolleAus.jpg

      153,4 kB, 1.168×776, 155 mal angesehen

    newbie schrieb:

    dann wir es verständlicher?
    Kannst Du mal das ganze Projekt posten?
    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:

    Kannst Du mal das ganze Projekt posten?

    ja, bitte.



    RushDen schrieb:

    Du hast einen semantischen Fehler in der Funktion (Tipp: nachdem ein wert zurückgegeben worden ist, wird die funktion verlassen (und ja auch die for each schleife))

    OK...ich sehe nicht was falsch ist :S
    Dateien
    • ZurLaufzeit.rar

      (170,47 kB, 105 mal heruntergeladen, zuletzt: )

    RushDen schrieb:

    Du hast einen semantischen Fehler in der Funktion (Tipp: nachdem ein wert zurückgegeben worden ist, wird die funktion verlassen (und ja auch die for each schleife))


    Ich stehe aufm Schlauch..

    VB.NET-Quellcode

    1. Private Function find_shape(ByVal myPos As Point) As Boolean
    2. For Each Shapepunkte As Point In PunkteListe 'Durchlaufe alle Koordinaten in der Punkteliste: {X=16,Y=48} {X=57,Y=39} {X=85,Y=67}
    3. If myPos.X = Shapepunkte.X And myPos.Y = Shapepunkte.Y Then 'Wenn myPos.X (aktuelle Mausposition X) = ein Punkt (X) in der Liste entspricht UND das gleiche für Y, dann..
    4. Return True 'Gib True zurück
    5. Else
    6. Return False 'Ansonsten False
    7. End If
    8. Next
    9. Return Nothing
    10. End Function

    Was ist daran falsch?

    Edit:

    Omg habs gefunden....tut mir leid :/

    VB.NET-Quellcode

    1. Private Function find_shape(ByVal myPos As Point) As Boolean
    2. For Each Shapepunkte As Point In PunkteListe 'Durchlaufe alle Koordinaten in der Punkteliste: {X=16,Y=48} {X=57,Y=39} {X=85,Y=67}
    3. If myPos.X = Shapepunkte.X And myPos.Y = Shapepunkte.Y Then 'Wenn myPos.X (aktuelle Mausposition X) = ein Punkt (X) in der Liste entspricht UND das gleiche für Y, dann..
    4. Return True 'Gib True zurück
    5. 'Else...........einfach weglassen
    6. 'Return False 'Ansonsten False.........einfach weglassen
    7. End If
    8. Next
    9. Return Nothing
    10. End Function


    Jetzt funktioniert es....

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

    debugge das mal im Einzelschritt, dann stellst du fest, dass die Schleife ausnahmslos bereits inne ersten Runde verlassen wird.
    Falls du nicht weißt, was ich mit Einzelschritt meine, gugge VisualStudio richtig nutzen (Google ist nicht deine Mami) , das mit dem Haltepunkt.