none
Verwendung von Protected Overrides Sub OnPaint in abgeleiteter Klasse RRS feed

  • 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


    Donnerstag, 28. Juni 2012 09:26

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
    Donnerstag, 28. Juni 2012 10:41
    Beantworter
  • 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 Sub

    Wobei 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
    Donnerstag, 28. Juni 2012 13:55
    Beantworter

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
    Donnerstag, 28. Juni 2012 10:41
    Beantworter
  • 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



    Donnerstag, 28. Juni 2012 12:40
  • 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 Sub

    Wobei 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
    Donnerstag, 28. Juni 2012 13:55
    Beantworter
  • Hallo Elmar,

    danke sehr. Jetzt weis ich den Weg.

    Gruss Ellen


    Ich benutze/ I'm using VB2008 & VB2010

    Donnerstag, 28. Juni 2012 14:46