Benutzer mit den meisten Antworten
Verwendung von Protected Overrides Sub OnPaint in abgeleiteter Klasse

Frage
-
Hallo zusammen,
an dieser Stelle komme ich nicht so richtig weiter. Ich brauche Eure Hilfe.
Zunächst code Form1:
Public class Form1 Private grafic As Graphics Private imgStart As Image = My.Resources.Add Private imgEnd As Image = My.Resources.Delete ''' <summary> ''' mouse click, select points ''' </summary> Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As EventArgs) Handles PictureBox1.MouseClick 'get mousebutton pressed Dim bMouse As MouseButtons = DirectCast(e, MouseEventArgs).Button 'get mouselocation Dim pt As Point = DirectCast(e, MouseEventArgs).Location 'calculate the center of the image and adjust it to mouse location Dim locationImage As Point = Point.Subtract(pt, New Size(Me.imgStart.Width \ 2, Me.imgStart.Height \ 2)) 'draw image If bMouse = Windows.Forms.MouseButtons.Left Then Me.grafic.DrawImage(Me.imgStart, locationImage) ElseIf bMouse = Windows.Forms.MouseButtons.Right Then Me.grafic.DrawImage(Me.imgEnd, locationImage) If currentLine < maxLines Then currentLine += 1 End If End Sub End Class
Bei mouseclick wird ein kleines image auf die Fläche gemalt. Das funktioniert aber nicht optimal. Immer wenn ich die Form verändere wird nicht neu gezeichnet. Dann nehmen wir doch das Onpaint event. Habe eine neue Pricturebox abgeleitet und OnPaint überschrieben:
Public Class myPicturebox Inherits System.Windows.Forms.PictureBox Protected Overrides Sub OnPaint(ByVal pe As System.Windows.Forms.PaintEventArgs) 'wie kann ich das hier einbauen MyBase.OnPaint(pe) End Sub End Class
Das Problem ist: Die mouse events bearbeite ich in der Klasse Form1 und da soll der code auch bleiben. Ich kann doch nicht den ganzen code dahin kopieren. Gibt es eine Möglichkeit die Methode OnPaint der Picturebox in die Klasse Form1 umzuleiten?
Gruss Ellen
Habe was vergesssen: Me.grafic wird in Form1:load instanziert:
Me.grafic = PictureBox1.CreateGraphics()
Ich benutze/ I'm using VB2008 & VB2010
- Bearbeitet Ellen Ramcke Donnerstag, 28. Juni 2012 09:47
Antworten
-
Hallo Ellen,
kapseln sollte man die Grafiken, die angezeigt werden, so dass die erweiterte PictureBox autonom arbeiten kann.
Ich habe im Beispiel das MouseDown Ereignis anstatt des Click-Ereignisses gewählt,
da dort die notwendigen Parameter als Argumente übergeben werden.Imports System.ComponentModel Public Class ClickPictureBox Inherits PictureBox Private _leftClickImage As Image Private _rightClickImage As Image Private _currentImage As Image = Nothing Private _currentLocation As Point = Point.Empty <Category("Behavior")> <Description("Grafik die angezeigt wird, wenn die linke Maustaste betätigt wird.")> Public Property LeftClickImage As Image Get Return _leftClickImage End Get Set(value As Image) If Not Object.ReferenceEquals(value, _leftClickImage) Then _leftClickImage = value RaiseEvent LeftClickImageChanged(Me, EventArgs.Empty) End If End Set End Property Public Event LeftClickImageChanged As EventHandler <Category("Behavior")> <Description("Grafik die angezeigt wird, wenn die rechte Maustaste betätigt wird.")> Public Property RightClickImage As Image Get Return _rightClickImage End Get Set(value As Image) If Not Object.ReferenceEquals(value, _leftClickImage) Then _rightClickImage = value RaiseEvent RightClickImageChanged(Me, EventArgs.Empty) End If End Set End Property Public Event RightClickImageChanged As EventHandler Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs) MyBase.OnMouseDown(e) If Me.DesignMode Then Return End If If (e.Button = MouseButtons.Left OrElse e.Button = MouseButtons.Right) Then If e.Button = Windows.Forms.MouseButtons.Left Then Me._currentImage = Me._leftClickImage ElseIf e.Button = Windows.Forms.MouseButtons.Right Then Me._currentImage = Me._rightClickImage End If If Me._currentImage IsNot Nothing Then Me._currentLocation = Point.Subtract( New Point(e.X, e.Y), New Size(Me._currentImage.Width \ 2, Me._currentImage.Height \ 2)) Me.Invalidate() End If End If End Sub Protected Overrides Sub OnPaint(pe As System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(pe) If Me._currentImage IsNot Nothing Then pe.Graphics.DrawImage(Me._currentImage, Me._currentLocation) End If End Sub End Class
Das ganze ist jetzt Quick 'n Dirty runtergetippt und so gut wie nicht getestet.
So müsste vermutlich die Position beim Resizen neu berechnet werden -
ich habe Deinen Code erstmal direkt übernommen.Das Click-Ereignis der (Click)PictureBox darfst Du weiterhin im Formular behandeln,
z. B. um die CurrentLine zu erhöhen, die keinen direkten Bezug auf die Grafiken hat.Gruß Elmar
- Als Antwort markiert Ellen Ramcke Donnerstag, 28. Juni 2012 14:46
-
Hallo Ellen,
im Prinzip geht es mit den Linien wie mit dem Bildchen.
Machen aus LineData eine "richtige" Klasse und verwende sie in der PictureBox,
OnPaint kann dann auch das Zeichnen übernehmen:Public Class LineData
' Alternativ Friend, gestrichen gedoppeltes Line in Properties Public Property Points As New List(Of Point) Public Capture As String Public Color As Color End Class ' ... in der PictureBox Klasse Public Property Lines As LineData Protected Overrides Sub OnPaint(pe As System.Windows.Forms.PaintEventArgs) ' "Malt" das Image MyBase.OnPaint(pe) For Each line In Lines ' Zeichnen der Linien Next ' Am Ende damit sie über den Linen sind If Me._currentImage IsNot Nothing Then pe.Graphics.DrawImage(Me._currentImage, Me._currentLocation) End If End SubWobei das ein grober Rohentwurf ist (denn ich muss Rasen mähen, bevor der Regen wiederkommt ;-).
Größter Schönheitsfehler ist, dass sich das Steuerelement nicht automatisch neu zeichnet,
wenn eine Linie hinzugefügt/entfernt wird, oder die anderen Eigenschaften (Color, ...) sich ändern.
Dazu müsste man wie jeweils Invalidate an das Steuerelement senden.
Am einfachsten wäre das zu realisieren, wenn man anstatt List(Of) eine ObservableCollection
verwendet. Denn damit hat man schon mal die Benachrichtigung für die Linien.
Bei den skalaren Eigenchaften ginge es wie bei den Images.
Nun noch ein Sub New(Owner As Control) und Du wärst (fast) fertig.Und ja: Die Reihenfolge beim OnPaint ist hier wichtig.
Wenn Du ein Image in der PictureBox hast, so wird das in MyBase.OnPaint gezeichnet.
Und der eigene Teil soll ja normalerweise darüber gezeichnet werden.Gruß Elmar
- Als Antwort markiert Ellen Ramcke Donnerstag, 28. Juni 2012 14:46
Alle Antworten
-
Hallo Ellen,
kapseln sollte man die Grafiken, die angezeigt werden, so dass die erweiterte PictureBox autonom arbeiten kann.
Ich habe im Beispiel das MouseDown Ereignis anstatt des Click-Ereignisses gewählt,
da dort die notwendigen Parameter als Argumente übergeben werden.Imports System.ComponentModel Public Class ClickPictureBox Inherits PictureBox Private _leftClickImage As Image Private _rightClickImage As Image Private _currentImage As Image = Nothing Private _currentLocation As Point = Point.Empty <Category("Behavior")> <Description("Grafik die angezeigt wird, wenn die linke Maustaste betätigt wird.")> Public Property LeftClickImage As Image Get Return _leftClickImage End Get Set(value As Image) If Not Object.ReferenceEquals(value, _leftClickImage) Then _leftClickImage = value RaiseEvent LeftClickImageChanged(Me, EventArgs.Empty) End If End Set End Property Public Event LeftClickImageChanged As EventHandler <Category("Behavior")> <Description("Grafik die angezeigt wird, wenn die rechte Maustaste betätigt wird.")> Public Property RightClickImage As Image Get Return _rightClickImage End Get Set(value As Image) If Not Object.ReferenceEquals(value, _leftClickImage) Then _rightClickImage = value RaiseEvent RightClickImageChanged(Me, EventArgs.Empty) End If End Set End Property Public Event RightClickImageChanged As EventHandler Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs) MyBase.OnMouseDown(e) If Me.DesignMode Then Return End If If (e.Button = MouseButtons.Left OrElse e.Button = MouseButtons.Right) Then If e.Button = Windows.Forms.MouseButtons.Left Then Me._currentImage = Me._leftClickImage ElseIf e.Button = Windows.Forms.MouseButtons.Right Then Me._currentImage = Me._rightClickImage End If If Me._currentImage IsNot Nothing Then Me._currentLocation = Point.Subtract( New Point(e.X, e.Y), New Size(Me._currentImage.Width \ 2, Me._currentImage.Height \ 2)) Me.Invalidate() End If End If End Sub Protected Overrides Sub OnPaint(pe As System.Windows.Forms.PaintEventArgs) MyBase.OnPaint(pe) If Me._currentImage IsNot Nothing Then pe.Graphics.DrawImage(Me._currentImage, Me._currentLocation) End If End Sub End Class
Das ganze ist jetzt Quick 'n Dirty runtergetippt und so gut wie nicht getestet.
So müsste vermutlich die Position beim Resizen neu berechnet werden -
ich habe Deinen Code erstmal direkt übernommen.Das Click-Ereignis der (Click)PictureBox darfst Du weiterhin im Formular behandeln,
z. B. um die CurrentLine zu erhöhen, die keinen direkten Bezug auf die Grafiken hat.Gruß Elmar
- Als Antwort markiert Ellen Ramcke Donnerstag, 28. Juni 2012 14:46
-
Hallo Elmar,
vielen Dank für Deine Anwort. Ich beschäftige mich mal wieder mit GDI. Das ganze soll dazu dienen Messwerte, die aus einer Datenbank kommen als Verteilungskurven grafisch darzustellen. Hier der derzeitige Arbeitsstand:
Das ist noch eine beta/demo Version. Die dicken Klötze sollen Grafiken mit 2x2 pixel werden und mit einer Linie verbunden werden. Nur das funktioniert noch nicht richtig. Das Bild wird nicht immer neu gezeichnet.
Fazit: Es gibt nur den sinnvollen Weg im OnPaint event zu arbeiten. Alles was ich in der Klasse brauche, muss ich mir über Schnittstellen beschaffen (properties). OK
Zwei Fragen habe ich noch:
MyBase.OnPaint(pe) If Me._currentImage IsNot Nothing Then pe.Graphics.DrawImage(Me._currentImage, Me._currentLocation) End If End Sub
1. Macht es einen Unterschied wenn ich die Zeile MyBase.OnPaint(pe) vor die Ausgabe oder hinter die Grafikausgabe stelle.
2. Wie kann ich Liniendaten, die in der Klasse Form1 entstehen in die andere bekommen. Ich brauche sie aber auch in der Klasse Form1. Die Daten sehen so aus:
''' <summary> ''' declare a structured data object for one line ''' </summary> Private Class lineData Friend linePt As New List(Of Point) Friend lineCapture As String Friend lineColor As Color End Class ''' <summary> ''' make a array of class linedata ''' </summary> Private mylines() As lineData = {New lineData With {.lineColor = Color.Brown, .lineCapture = "Line1"}, _ New lineData With {.lineColor = Color.Cyan, .lineCapture = "Line2"}, _ New lineData With {.lineColor = Color.Indigo, .lineCapture = "Line3"}, _ New lineData With {.lineColor = Color.Blue, .lineCapture = "Line4"}, _ New lineData With {.lineColor = Color.Red, .lineCapture = "Line5"}, _ New lineData With {.lineColor = Color.Yellow, .lineCapture = "Line6"}, _ New lineData With {.lineColor = Color.YellowGreen, .lineCapture = "Line7"}, _ New lineData With {.lineColor = Color.Coral, .lineCapture = "Line8"}, _ New lineData With {.lineColor = Color.Pink, .lineCapture = "Line9"}, _ New lineData With {.lineColor = Color.Orange, .lineCapture = "Line10"} _ }
Auf jeden Fall benötige ich den Typ Linedata auch in der Picturebox. Sonst geht das ja nicht.
Gruss Ellen
Eine Frage habe noch vergessen: MyBase.OnMouseDown(e)
Kommt das Event auch in der Klasse Form1 an?
Ich benutze/ I'm using VB2008 & VB2010
- Bearbeitet Ellen Ramcke Donnerstag, 28. Juni 2012 12:51
-
Hallo Ellen,
im Prinzip geht es mit den Linien wie mit dem Bildchen.
Machen aus LineData eine "richtige" Klasse und verwende sie in der PictureBox,
OnPaint kann dann auch das Zeichnen übernehmen:Public Class LineData
' Alternativ Friend, gestrichen gedoppeltes Line in Properties Public Property Points As New List(Of Point) Public Capture As String Public Color As Color End Class ' ... in der PictureBox Klasse Public Property Lines As LineData Protected Overrides Sub OnPaint(pe As System.Windows.Forms.PaintEventArgs) ' "Malt" das Image MyBase.OnPaint(pe) For Each line In Lines ' Zeichnen der Linien Next ' Am Ende damit sie über den Linen sind If Me._currentImage IsNot Nothing Then pe.Graphics.DrawImage(Me._currentImage, Me._currentLocation) End If End SubWobei das ein grober Rohentwurf ist (denn ich muss Rasen mähen, bevor der Regen wiederkommt ;-).
Größter Schönheitsfehler ist, dass sich das Steuerelement nicht automatisch neu zeichnet,
wenn eine Linie hinzugefügt/entfernt wird, oder die anderen Eigenschaften (Color, ...) sich ändern.
Dazu müsste man wie jeweils Invalidate an das Steuerelement senden.
Am einfachsten wäre das zu realisieren, wenn man anstatt List(Of) eine ObservableCollection
verwendet. Denn damit hat man schon mal die Benachrichtigung für die Linien.
Bei den skalaren Eigenchaften ginge es wie bei den Images.
Nun noch ein Sub New(Owner As Control) und Du wärst (fast) fertig.Und ja: Die Reihenfolge beim OnPaint ist hier wichtig.
Wenn Du ein Image in der PictureBox hast, so wird das in MyBase.OnPaint gezeichnet.
Und der eigene Teil soll ja normalerweise darüber gezeichnet werden.Gruß Elmar
- Als Antwort markiert Ellen Ramcke Donnerstag, 28. Juni 2012 14:46